通过协议扩展使用具有协议方法的默认实现的选择器

时间:2017-05-16 19:40:50

标签: ios objective-c swift uiviewcontroller selector

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

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的参数是指实例方法&keyboard;' keyboardWillShow'没有暴露于Objective-C。"

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

有谁知道实现这一目标的方法?我乍一看似乎没有办法解决它,但我也知道UIViewController是NSObject的孩子,并且通常允许UIViewControllers上的实例方法成为选择器的目标。我想通过在我的协议扩展上放置约束,要求它是UIViewController的子类,我可以使用选择器来定位其中包含的默认实现。

思想?

2 个答案:

答案 0 :(得分:3)

无法完成。

每个人都首先认为这是一个很酷的主意,(包括我自己),但它根本无法做到。

Swift协议扩展对于Objective-C是不可见的。对此无能为力。即使#selector无法正常工作,因为:

您的协议功能仅在协议扩展中定义

人们一直试图通过协议扩展将Objective-C可调用功能(选择器,委托方法,等等)注入到类中。遗憾。

答案 1 :(得分: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)
   }
}