在扩展方法中将实例方法公开给 Objective-C

Exposing an instance method to Objective-C within an extension method

我有一个应用程序在几个不同的控制器中使用 UIKeyboardWillShow & Hide 通知。我决定尝试将使用键盘移动视图所需的逻辑整合到基于协议的扩展中。

这是我的协议

public protocol KeyboardType : class {
    func keyboardWillShow(_ sender: Notification)
    func keyboardWillHide(_ sender: Notification)
}

接下来,我为我的新协议添加了一个扩展,这样我需要做的就是实现我的“KeyboardType 协议,我将获得必要的功能,以使用我的键盘移动我的视图:

这是我的分机

public extension KeyboardType where Self: UIViewController {

    func addObservers() { 
        NotificationCenter.default.addObserver(self, selector: #selector(Self.keyboardWillShow(_:)), name:NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
        NotificationCenter.default.addObserver(self, selector: #selector(Self.keyboardWillHide(_:)), name:NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
    }

    func removeObservers() {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)

    }

    func keyboardWillHide(_ sender: Notification) {
        let userInfo: [AnyHashable : Any] = (sender as NSNotification).userInfo!
        let keyboardSize: CGSize = (userInfo[UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue.size
        self.view.frame.origin.y += keyboardSize.height
    }

    func keyboardWillShow(_ sender: Notification) {
        let userInfo: [AnyHashable : Any] = sender.userInfo!

        let keyboardSize: CGSize = (userInfo[UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue.size
        let offset: CGSize = (userInfo[UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size

        if keyboardSize.height == offset.height {
            if self.view.frame.origin.y == 0 {
                UIView.animate(withDuration: 0.1, animations: { () -> Void in
                    self.view.frame.origin.y -= keyboardSize.height
                })
            }
        } else {
            UIView.animate(withDuration: 0.1, animations: { () -> Void in
                self.view.frame.origin.y += keyboardSize.height - offset.height
            })
        }
    }

}

问题

问题是编译器要求我将@objc 添加到我的 keyboardWillShow 和 keyboardWillHide 方法中。当我允许 Xcode 添加关键字时,编译器立即要求我删除 @objc 关键字。

Argument of '#selector' refers to instance method 'keyboardWillShow' that is not exposed to Objective-C

我的问题

在这种情况下如何将 keyboardWillShow 公开给 Objective-C?

有没有更好的方法来完成同样的任务?

你试过了吗?:

@objc public protocol KeyboardType {
    func keyboardWillShow(_ sender: Notification)
    func keyboardWillHide(_ sender: Notification)
}

您还需要导入 UIKit

我会采取不同的方法扩展 UIViewController,如下所示:

protocol KeyboardController {
    func keyboardWillShow(_ sender: Notification)
    func keyboardWillHide(_ sender: Notification)
}

extension UIViewController: KeyboardController {
    func addObservers() {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillShow(_:)),
                                               name:.UIKeyboardWillShow,
                                               object: view.window)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillHide(_:)),
                                               name:.UIKeyboardWillHide,
                                               object: view.window)
    }

    func removeObservers() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: view.window)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: view.window)
    }

    func keyboardWillHide(_ notification: Notification) {
        print("---> keyboardWillHide")

        guard let userInfo = notification.userInfo else { return }
        let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
        print("keyboardSize", keyboardSize)

    }

    func keyboardWillShow(_ notification: Notification) {
        print("---> keyboardWillShow")
        guard

            let userInfo = notification.userInfo,
            let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size,
            let offset = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
        else { return }

        if keyboardSize.height == offset.height {
            if self.view.frame.origin.y == 0 {
                UIView.animate(withDuration: 0.1, animations: { () -> Void in
                    self.view.frame.origin.y -= keyboardSize.height
                })
            }
        } else {
            UIView.animate(withDuration: 0.1, animations: { () -> Void in
                self.view.frame.origin.y += keyboardSize.height - offset.height
            })
        }
    }

}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        addObservers()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

另一个也是我最喜欢的选项是子类化 UIViewController:

class KeyboardViewController: UIViewController {

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        addObservers()
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        removeObservers()
    }

    func addObservers() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: view.window)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: view.window)
    }

    func removeObservers() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: view.window)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: view.window)
    }

    func keyboardWillHide(_ notification: Notification) {
        print("---> keyboardWillHide")
        if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size.height {
            print("keyboardHeight", keyboardHeight)
            view.frame.origin.y += keyboardHeight
        }
    }

    func keyboardWillShow(_ notification: Notification) {
        print("---> keyboardWillShow")
        if let userInfo = notification.userInfo,
            let keyboardHeight = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size.height,
            let offsetHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height {
            print("userInfo", userInfo)
            print("keyboardHeight", keyboardHeight)
            print("offsetHeight", offsetHeight)
            if keyboardHeight == offsetHeight {
                if self.view.frame.origin.y == 0 {
                    UIView.animate(withDuration: 0.1, animations: { () -> Void in
                        self.view.frame.origin.y -= keyboardHeight
                    })
                }
            } else {
                UIView.animate(withDuration: 0.1, animations: { () -> Void in
                    self.view.frame.origin.y += keyboardHeight - offsetHeight
                })
            }
        }
    }
}

class ViewController:  KeyboardViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
       // Dispose of any resources that can be recreated.
    }
}