协议扩展编译器错误中的Swift 2.2 #selector

And*_*rea 25 protocols ios swift

我有一个协议扩展,它曾经在swift 2.2之前完美地工作.

现在我有一个警告,告诉我使用新的#selector,但如果我添加它

没有使用Objective-C Selector声明的方法.

我尝试在这几行代码中重现这个问题,可以很容易地复制并粘贴到游乐场

  protocol Tappable {
    func addTapGestureRecognizer()
    func tapGestureDetected(gesture:UITapGestureRecognizer)
}

extension Tappable where Self: UIView {
    func addTapGestureRecognizer() {
        let gesture = UITapGestureRecognizer(target: self, action:#selector(Tappable.tapGestureDetected(_:)))
        addGestureRecognizer(gesture)
    }
}

class TapView: UIView, Tappable {
    func tapGestureDetected(gesture:UITapGestureRecognizer) {
        print("Tapped")
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一个建议在协议中附加到该方法@objc,但是如果我这样做也要求我将它添加到实现它的类中,但是一旦我添加该类就不再符合协议,因为它不会好像在协议扩展中看到了实现.
我该如何正确实现?

小智 24

我遇到了类似的问题.这就是我做的.

  1. 将协议标记为@objc.
  2. 将我使用默认行为扩展的任何方法标记为可选.
  3. 然后使用Self.在#selector中.

    @objc public protocol UpdatableUserInterfaceType {
      optional func startUpdateUITimer()
      optional var updateInterval: NSTimeInterval { get }
      func updateUI(notif: NSTimer)
    }
    
    public extension UpdatableUserInterfaceType where Self: ViewController {
    
      var updateUITimer: NSTimer {
        return NSTimer.scheduledTimerWithTimeInterval(updateInterval, target: self, selector: #selector(Self.updateUI(_:)), userInfo: nil, repeats: true)
      }
    
      func startUpdateUITimer() {
        print(updateUITimer)
      }
    
      var updateInterval: NSTimeInterval {
        return 60.0
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)


小智 17

您可以创建一个属性作为选择器...示例:

protocol Tappable {
    var selector: Selector { get }
    func addTapGestureRecognizer()
}

extension Tappable where Self: UIView {
    func addTapGestureRecognizer() {
        let gesture = UITapGestureRecognizer(target: self, action: selector)
        addGestureRecognizer(gesture)
    }
}

class TapView: UIView, Tappable {
    var selector = #selector(TapView.tapGestureDetected(_:))

    func tapGestureDetected(gesture:UITapGestureRecognizer) {
        print("Tapped")
    }
}
Run Code Online (Sandbox Code Playgroud)

错误停止显示,并且不再需要使用@objc装饰器设置协议和类.

这个解决方案不是最优雅的,但直到现在看起来还不错.


Pee*_*eep 7

这个答案与Bruno Hecktheuers非常相似,但是我们选择将它作为参数传递给addTapGestureRecognizer函数,而不是让每个想要符合"Tappable"协议的人实现变量"selector".

protocol Tappable {
    func addTapGestureRecognizer(selector selector: Selector)
    func tapGestureDetected(gesture:UITapGestureRecognizer)
}

extension Tappable where Self: UIView {
    func addTapGestureRecognizer(selector selector: Selector)
        let gesture = UITapGestureRecognizer(target: self, action: selector)
        addGestureRecognizer(gesture)
    }
}

class TapView: UIView, Tappable {    
    func tapGestureDetected(gesture:UITapGestureRecognizer) {
        print("Tapped")
    }
}
Run Code Online (Sandbox Code Playgroud)

然后只需将选择器传递到任何地方:

addTapGestureRecognizer(selector: #selector(self.tapGestureDetected(_:)))
Run Code Online (Sandbox Code Playgroud)

这样我们就可以避免让实现此协议的人必须实现选择器变量,并且我们也避免使用"@objc"标记每个人使用此协议.像这种方法的感觉不那么臃肿.

  • 如果要在协议扩展本身中提供`tapGestureDetected`的默认实现,而不是在实现协议的具体类中,该怎么办? (4认同)