【问题标题】:Exposing an instance method to Objective-C within an extension method在扩展方法中向 Objective-C 公开实例方法
【发布时间】:2017-01-11 04:18:25
【问题描述】:

我有一个应用程序在几个不同的控制器中使用 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 关键字。

“#selector”的参数指的是实例方法“keyboardWillShow” 没有暴露给 Objective-C

我的问题

在这种情况下如何将keyboardWillShow 暴露给Objective-C?

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

【问题讨论】:

  • 注意:与选择器无关,但您现在的方法将在 beta 6 中崩溃。您需要将其转换为 NSValue (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
  • @LeoDabus 这个确切的代码在我的 beta 6 视图控制器中完美运行
  • 那很奇怪,我会仔细检查。当我更新到 beta 6 时,我的代码在转换为 AnyObject 时崩溃了
  • 我想通了。您的方法有效,因为您首先要转换为 NSNotification。如果您不强制转换为 NSNotification 并且您需要按照我的建议进行操作,否则它会导致您的应用崩溃
  • 您的屏幕截图显示您只导入 Foundation,但您知道需要导入 UIKit 才能在代码中使用 UIViewController。一些错误(包括缺少导入)可能会阻止 Xcode 进行代码分析,因此会显示旧的错误消息。

标签: xcode selector extension-methods swift3


【解决方案1】:

我会采取不同的方法来扩展 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.
    }
}

【讨论】:

  • 我最初考虑过扩展 UIViewController,但我试图避免向 UIViewController 类型添加一揽子功能。我认为这是一个可以接受的解决方案,但如果我接受这个作为我的答案,我需要重新提出我的问题。 (我可能会这样做)
  • 我接受这是解决我问题的最佳方法。我会查看您明天发送的 Dropbox 文件,然后修改我的问题。再次感谢狮子座
  • 快速查看了您的文件,子类化路线也是一个很好的解决方案
【解决方案2】:

你试过了吗?:

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

你还需要导入 UIKit

【讨论】:

  • 是的。当我添加这个时,编译器尝试在我的两个函数之前添加@objc,当我添加它们时,它会告诉我删除它们。
  • @DanBeaulieu 您是否尝试将其添加到协议而不是方法中?
  • @DanBeaulieu,一种奇怪的行为。在我的 beta 6 中,Xcode 不会对上面显示的协议抱怨 @objc。 (尽管您可能需要修复代码的其他部分。)在上面重新输入我的代码并尝试 clean-build。
  • @LeoDabus 我添加了一张图片并简要说明了正在发生的事情。上面的解决方案是我在发布之前尝试过的。
猜你喜欢
  • 1970-01-01
  • 2019-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多