我可以让#selector引用Swift中的闭包吗?

Bla*_*ard 23 closures selector swift

我想让selector我的方法的参数引用一个闭包属性,它们都存在于同一范围内.例如,

func backgroundChange() {
    self.view.backgroundColor = UIColor.blackColor()
    self.view.alpha = 0.55

    let backToOriginalBackground = {
        self.view.backgroundColor = UIColor.whiteColor()
        self.view.alpha = 1.0
    }

    NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(backToOriginalBackground), userInfo: nil, repeats: false)
}
Run Code Online (Sandbox Code Playgroud)

但是,这显示错误:Argument of #selector cannot refer to a property.

当然,我可以定义一个新的,单独的方法并将闭包的实现移动到它,但我想为这么小的实现保持节俭.

是否可以设置闭包#selector参数?

wer*_*ver 29

不是直接的,但可能有一些变通方法.看一下下面的例子.

/// 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)
Run Code Online (Sandbox Code Playgroud)

  • 注意:您必须保留按钮将使用的操作的实例;该按钮不拥有它的所有权。 (4认同)

sch*_*her 11

我至少尝试过UIBarButtonItem:

private var actionKey: Void?

extension UIBarButtonItem {

    private var _action: () -> () {
        get {
            return objc_getAssociatedObject(self, &actionKey) as! () -> ()
        }
        set {
            objc_setAssociatedObject(self, &actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    convenience init(title: String?, style: UIBarButtonItemStyle, action: @escaping () -> ()) {
        self.init(title: title, style: style, target: nil, action: #selector(pressed))
        self.target = self
        self._action = action
    }

    @objc private func pressed(sender: UIBarButtonItem) {
        _action()
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, action: {
    print("Hello World!")
})
Run Code Online (Sandbox Code Playgroud)

  • 这非常方便,并且可以作为 Swift 实现保留。谢谢 (3认同)

Rob*_*ier 8

正如@ gnasher729所指出的那样,这是不可能的,因为选择器只是方法的名称,而不是方法本身.在一般情况下,我会dispatch_after在这里使用,但在这种特殊情况下,更好的工具IMO UIView.animateWithDuration,因为它正是该功能的用途,并且很容易调整转换:

UIView.animateWithDuration(0, delay: 0.5, options: [], animations: {
    self.view.backgroundColor = UIColor.whiteColor()
    self.view.alpha = 1.0
}, completion: nil)
Run Code Online (Sandbox Code Playgroud)


Cha*_*tas 6

现在有可能。我在Swift 4中为基于块的选择器创建了要点。

https://gist.github.com/cprovatas/98ff940140c8744c4d1f3bcce7ba4543

用法:

UIButton().addTarget(Selector, action: Selector { debugPrint("my code here") }, for: .touchUpInside)

  • 您必须导入 obj-c 类才能使其工作 (2认同)