带有按钮和选择器的协议扩展

Protocol extension with button and selector

我有一些协议:

@objc protocol SomeProtocol { } 

我扩展了 UIViewController 个实例。在此扩展中,我想创建并添加一个按钮,其选择器也在协议中定义:

extension SomeProtocol where Self: UIViewController {

    func addSomeButton() {
        let someButton = UIButton()
        someButton.addTarget(self, #selector(someButtonPressed), for: .touchUpInside)
        view.addSubview(someButton)
    }

    @objc func someButtonPressed() {
    }

}

但是,我收到错误 @objc can only be used with members of 类、@objc protocols 和 类 的具体扩展someButtonPressed.

的定义

有什么方法可以使用协议来实现吗?

提前感谢您的任何建议!

您需要在协议中提供 Selector 要求。这是因为您只能将 @objc 属性应用于 NSObject。标记协议 @objc 的唯一原因是为了可选方法。

@objc protocol SomeProtocol {
    var action: Selector { get }
} 

将扩展名更改为:

extension SomeProtocol where Self: UIViewController {
    func addSomeButton() {
        let someButton = UIButton()
        someButton.addTarget(self, action: action, for: .touchUpInside)
        view.addSubview(someButton)
    }
}

现在,这有效:

extension UIViewController: SomeProtocol {
    @objc func buttonPressed(sender: UIButton) {
        print("Button pressed")
    }
    var action: Selector {
        return #selector(buttonPressed(sender:))
    }
}

用法:

let vc: myViewController: MyViewController!
func doSomething() {
  vc.addSomeButton()
}

一种解决方法是向 UIButton 添加一个封闭套管而不是此处显示的目标操作 并为方便起见复制在下面。

typealias Closure = () -> ()

///
class ClosureSleeve {
    let closure: Closure
    init(_ closure: @escaping Closure) {
        self.closure = closure
    }
    @objc func invoke () {
        closure()
    }
}

extension UIControl {
    func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping Closure) {
        let sleeve = ClosureSleeve(closure)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
        objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}

然后简单地替换:

someButton.addTarget(self, #selector(someButtonPressed), for: .touchUpInside)

与:

someButton.addAction { [weak self] in
        self?.someButtonPressed()
    }

嘿嘿。