【问题标题】:Using selectors with default implementations of protocol methods via protocol extensions通过协议扩展将选择器与协议方法的默认实现一起使用
【发布时间】:2017-05-16 19:40:49
【问题描述】:

我正在尝试制定一个 ViewController 可以实现的协议,以调整其视图以适应键盘显示/隐藏。

protocol KeyboardAdaptable {
    func keyboardWillShow(notification: NSNotification)
    func keyboardWillHide(notification: NSNotification)
    func addKeyboardNotificationObservers()
}

extension KeyboardAdaptable where Self: UIViewController, Self: NSObject {
    func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y == 0{
                self.view.frame.origin.y -= keyboardSize.height
            }
        }
    }

    func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y != 0{
                self.view.frame.origin.y += keyboardSize.height
            }
        }
    }

    func addKeyboardNotificationObservers() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
}

错误:“#selector 的参数引用了未暴露给 Objective-C 的实例方法 'keyboardWillShow'。”

我知道选择器是 Objective-C 的一个特性,并且引用的函数必须是兼容的。我试图通过用@objc 注释标记协议本身以及方法来解决这个问题,但是编译器坚持我也用@objc 标记协议扩展中的默认实现。当我这样做时,它向我大喊要删除 @objc 注释,因为 “@objc 只能用于类的成员、@objc 协议和类的具体扩展”(即不在协议中扩展?)

有人知道实现这一目标的方法吗?我知道乍一看似乎没有办法绕过它,但我也知道 UIViewController 是 NSObject 的子对象,通常 UIViewControllers 上的实例方法被允许成为选择器的目标。我认为通过对我的协议扩展施加约束,要求它是 UIViewController 的子类,我可以使用选择器来定位其中包含的默认实现。

想法?

【问题讨论】:

    标签: ios objective-c swift uiviewcontroller selector


    【解决方案1】:

    做不到。

    每个人首先都认为这是一个很酷的想法(包括我自己),但它根本不可能。

    Swift 协议扩展对 Objective-C 是不可见的。对此无能为力。即使使用#selector 也不起作用,因为:

    您的协议函数在协议扩展中定义。

    人们不断尝试通过协议扩展将 Objective-C 可调用功能(选择器、委托方法等)注入到类中。对不起。

    【讨论】:

    • 无需抱歉——负面的发现也是好的发现。也许这个问题可以防止其他一些可怜的灵魂浪费时间试图太聪明了一半! :) 我有一种感觉是这种情况,但想看看我是否遗漏了什么。我要等一天左右,看看有没有人能把兔子从帽子里拉出来;那么我会将您的答案标记为正确。干杯。
    • 任何人都可以添加到我的答案中的唯一一件事是拥有一个由您的观察者调用的单独函数,然后调用委托方法。很多优秀的库都以这种方式使用它,如果您搜索过它们,您可能会注意到它。唯一目的是调用委托函数的函数。但直接 - 没办法。呃,好吧。干杯:)
    【解决方案2】:

    我完全同意前面的回答——这是不可能的。但是,我决定对您的代码进行一些处理,并创建我能找到的最可接受的解决方案。也许它会派上用场:

    class KeyboardAdapter {
    
       private weak var view: UIView!
    
       init(view: UIView) {
          self.view = view
          addKeyboardNotificationObservers()
       }
    
       @objc
       func keyboardWillShow(notification: NSNotification) {
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
             if self.view.frame.origin.y == 0 {
                self.view.frame.origin.y -= keyboardSize.height
             }
          }
       }
    
       @objc
       func keyboardWillHide(notification: NSNotification) {
          if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
             if self.view.frame.origin.y != 0 {
                self.view.frame.origin.y += keyboardSize.height
             }
          }
       }
    
       private func addKeyboardNotificationObservers() {
          NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
          NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
       }
    }
    
    protocol KeyboardAdaptable: class {
       var keyboardAdapter: KeyboardAdapter! { get set }
       func configureAdapter()
    }
    
    extension KeyboardAdaptable where Self: UIViewController {
       func configureAdapter() {
          keyboardAdapter = KeyboardAdapter(view: self.view)
       }
    }
    

    【讨论】:

    • 感谢您为此付出的努力!这是完成我想做的事情的好方法。
    • @Cognitio 很乐意提供帮助 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    相关资源
    最近更新 更多