简体   繁体   English

通过协议扩展将选择器与协议方法的默认实现一起使用

[英]Using selectors with default implementations of protocol methods via protocol extensions

I am attempting to make a Protocol that ViewControllers can implement to adjust their view to accommodate keyboard show/hide. 我正在尝试制定一个协议,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)
    }
}

The error: "Argument of #selector refers to instance methods 'keyboardWillShow' that is not exposed to Objective-C." 错误: “#selector的参数引用未公开给Objective-C的实例方法'keyboardWillShow'。”

I know that selectors are a feature of Objective-C and that referenced functions must be compatible. 我知道选择器是Objective-C的功能,并且引用的函数必须兼容。 I tried to solve this by marking the protocol itself as well as the methods with the @objc annotation, but then the compiler insisted I mark the default implementations in the protocol extension with @objc as well. 我试图通过标记协议本身以及带有@objc批注的方法来解决此问题,但是编译器坚持要求我也使用@objc标记协议扩展中的默认实现。 When I did that, it yelled at me to remove the @objc annotation because "@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes" (ie not in protocol extensions?) 当我这样做时,我大吼大叫删除@objc注释,因为“ @objc只能与类的成员,@ objc协议和类的具体扩展一起使用” (即,不能在协议扩展中使用?)

Does anyone know of a way to achieve this goal? 有人知道实现此目标的方法吗? I know at first blush it might seem like there is no way around it, but I also know that UIViewController is a child of NSObject, and typically instance methods on UIViewControllers are allowed to be targets for selectors. 我知道乍一看似乎没有办法解决,但我也知道UIViewController是NSObject的子级,通常UIViewControllers上的实例方法可以用作选择器的目标。 I thought by placing constraints on my protocol extension requiring it be a subclass of UIViewController, that I could target the default implementations contained therein with selectors. 我认为,通过对协议扩展施加约束(要求它是UIViewController的子类),我可以使用选择器来定位包含在其中的默认实现。

Thoughts? 思考?

Can't be done. 不能做

Everyone first thinks this would be a cool idea, (myself included) but it simply can't. 每个人首先认为这将是一个很酷的主意(包括我自己),但事实并非如此。

Swift protocol extensions are invisible to Objective-C. Swift协议扩展对Objective-C不可见。 Can't do anything about that. 对此无能为力。 Even with #selector doesn't work because: 即使使用#selector也不起作用,因为:

Your protocol functions are only defined in the protocol extension. 您的协议功能在协议扩展中定义。

People keep trying to inject Objective-C-callable functionality (selector, delegate method, whatever) into a class via a protocol extension. 人们一直试图通过协议扩展将可调用Objective-C的功能(选择器,委托方法等)注入类中。 Sorry. 抱歉。

I fully agree with the previous answer - this can't be done. 我完全同意先前的回答-无法完成。 However I decided to fool around a bit with your code and create the most acceptable solution I could find. 但是,我决定在您的代码中四处闲逛,并创建我能找到的最可接受的解决方案。 Maybe it will come in handy: 也许会派上用场:

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)
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM