I want to make a selector
argument of my method refer to a closure property, both of them exist in the same scope. For example,
func backgroundC
You can use ActionClosurable which support UIControl, UIButton, UIRefreshControl, UIGestureRecognizer and UIBarButtonItem. https://github.com/takasek/ActionClosurable
Bellow show example of UIBarButtonItem
// UIBarButtonItem
let barButtonItem = UIBarButtonItem(title: "title", style: .plain) { _ in
print("barButtonItem title")
}
@werediver's answer is excellent. Here's an update that allows you to call it as a function.
import Foundation
public extension Selector {
/// Wraps a closure in a `Selector`.
/// - Note: Callable as a function.
final class Perform: NSObject {
public init(_ perform: @escaping () -> Void) {
self.perform = perform
super.init()
}
private let perform: () -> Void
}
}
//MARK: public
public extension Selector.Perform {
@objc func callAsFunction() { perform() }
var selector: Selector { #selector(callAsFunction) }
}
You need to manage strong references to Selector.Perform
s. One way to do that is to subclass UIKit classes that were designed to work with target-action:
/// A `UITapGestureRecognizer` that wraps a closure.
public final class TapGestureRecognizer: UITapGestureRecognizer {
public init(_ perform: @escaping () -> Void) {
self.perform = .init(perform)
super.init(target: self.perform, action: self.perform.selector)
}
public let perform: Selector.Perform
}
let tapRecognizer = TapGestureRecognizer { print("
So my answer to having a selector
be assigned to a closure
in a swift like manner is similar to some of the answers already, but I thought I would share a real life example of how I did it within a UIViewController
extension.
fileprivate class BarButtonItem: UIBarButtonItem {
var actionCallback: ( () -> Void )?
func buttonAction() {
actionCallback?()
}
}
fileprivate extension Selector {
static let onBarButtonAction = #selector(BarButtonItem.buttonAction)
}
extension UIViewController {
func createBarButtonItem(title: String, action: @escaping () -> Void ) -> UIBarButtonItem {
let button = BarButtonItem(title: title, style: .plain, target nil, action: nil)
button.actionCallback = action
button.action = .onBarButtonAction
return button
}
}
// Example where button is inside a method of a UIViewController
// and added to the navigationItem of the UINavigationController
let button = createBarButtonItem(title: "Done"){
print("Do something when done")
}
navigationItem.setLeftbarButtonItems([button], animated: false)
Not directly, but some workarounds are possible. Take a look at the following example.
/// Target-Action helper.
final class Action: NSObject {
private let _action: () -> ()
init(action: @escaping () -> ()) {
_action = action
super.init()
}
@objc func action() {
_action()
}
}
let action1 = Action { print("action1 triggered") }
let button = UIButton()
button.addTarget(action1, action: #selector(action1.action), forControlEvents: .TouchUpInside)
If you change the scope of block to a class scope rather than function and hold a reference to closure there.
You could invoke that closure with a function. in the class. So that way you can invoke that closure as a selector.
Something like this:
class Test: NSObject {
let backToOriginalBackground = {
}
func backgroundChange() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false)
}
func test() {
self.backToOriginalBackground()
}
}
As @gnasher729 notes, this is not possible because selectors are just names of methods, not methods themselves. In the general case, I'd use dispatch_after
here, but in this particular case, the better tool IMO is UIView.animateWithDuration
, because it's exactly what that function is for, and it's very easy to tweak the transition:
UIView.animateWithDuration(0, delay: 0.5, options: [], animations: {
self.view.backgroundColor = UIColor.whiteColor()
self.view.alpha = 1.0
}, completion: nil)