【问题标题】:How to use a selector from another class?如何使用另一个类的选择器?
【发布时间】:2016-11-27 01:06:09
【问题描述】:

我有一个名为 FooFramework 的 Cocoa Touch 框架。

在其中,我想在键盘显示时管理选定视图在 Y 轴上的向上移动。我创建了一个KeyboardManager 类。下面是它的外观:

import UIKit

public class KeyboardManager {
    var notifyFromObject: Any?
    var observer: Any
    public var viewsToPushUp: [UIView] = []

    public init(observer: Any, viewsToPushUp: [UIView], notifyFromObject: Any? = nil) {
        self.observer = observer
        self.notifyFromObject = notifyFromObject
        self.viewsToPushUp = viewsToPushUp
    }

    public func pushViewsUpWhenKeyboardWillShow(){
        let notificationCenter = NotificationCenter.default
        print(self)
        notificationCenter.addObserver(self.observer, selector: #selector(FooFramework.KeyboardManager.pushViewsUp), name: NSNotification.Name.UIKeyboardWillShow, object: notifyFromObject)
    }

    @objc public func pushViewsUp(notification: NSNotification) {
        if let keyboardRectValue = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            let keyboardHeight = keyboardRectValue.height

            for view in viewsToPushUp {
                view.frame.origin.y -= keyboardHeight
            }
        }
    }
}

然后,我将这个 FooFramework 导入到一个名为 Bar 的 iOS 应用程序中。为了测试 FooFramework,我想推一个UITextField。代码如下:

import UIKit
import FooFramework

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let kb = KeyboardManager(observer: self, viewsToPushUp: [textField], notifyFromObject: nil)
        kb.pushViewsUpWhenKeyboardWillShow()
    }

    func pushViewsUp(notification: NSNotification) {
        print("This should not be printed")
    }
}

我的问题是This should not be printed 出现在控制台中,而KeyboardManager 中的pushViewsUp 方法永远不会被调用。尽管我为选择器使用了完全限定的名称,但它坚持使用 ViewController 中的 pushViewsUp。这让我发疯了。

如果我从 ViewController 中删除 pushViewsUp,我会收到以下错误:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Bar.ViewController pushViewsUpWithNotification:]: unrecognized selector sent to instance 0x7fc540702d80'

我需要做什么才能使选择器正确指向FooFramework.KeyboardManager.pushViewsUp

【问题讨论】:

    标签: swift swift3 selector ios10


    【解决方案1】:

    我相信您需要在addObserver 函数中使用self 而不是self.observer 作为观察者。

    您还需要在函数范围之外声明kb 变量,以便管理器检测到通知。

    例子:

    import UIKit
    import FooFramework
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var textField: UITextField!
    
        var kb: KeyboardManager?
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            kb = KeyboardManager(observer: self, viewsToPushUp: [textField], notifyFromObject: nil)
            kb?.pushViewsUpWhenKeyboardWillShow()
        }
    }
    

    KeyboardManager 变化:

    public func pushViewsUpWhenKeyboardWillShow() {
        let notificationCenter = NotificationCenter.default
        print(self)
        notificationCenter.addObserver(self, 
                                       selector: #selector(FooFramework.KeyboardManager.pushViewsUp), 
                                       name: NSNotification.Name.UIKeyboardWillShow, 
                                       object: notifyFromObject)
    }
    

    【讨论】:

    • 谢谢。我完全忽略了实际上是类本身在注册事件。
    【解决方案2】:

    除了贾斯汀建议的完全正确的建议之外,在您完全解决问题之前,还需要考虑一些其他事项。

    1. KeyBoardManager 类实例本身将观察keyboardWillMoveUp 通知,因此您的

    var 观察者:任意

    里面是没有必要的。你应该删除它。

    1. 我还将 addObserver 部分放在 KeyBoardManager 类本身的 init 中,这样就可以避免这个额外的调用 pushViewsUpWhenKeyboardWillShow() ,这似乎什么也没做。由于 KeyBoardManager 类应该只做这个,我不明白为什么添加观察者应该是另一个函数调用。

    这就是您的 KeyboardManager 类的外观:

    import UIKit
    
    public class KeyboardManager {
    
        var notifyFromObject: Any?
        public var viewsToPushUp: [UIView] = []
    
        public init(viewsToPushUp: [UIView], notifyFromObject: Any? = nil){
            self.notifyFromObject = notifyFromObject
            self.viewsToPushUp = viewsToPushUp
    
    //remember KeyboardManager itself is observing the notifications and moving the views it received from the ViewController. Hence we use self.
            NotificationCenter.default.addObserver(self, selector: #selector(FooFramework.KeyboardManager.pushViewsUp), name: NSNotification.Name.UIKeyboardWillShow, object: notifyFromObject)
        }
    
        @objc public func pushViewsUp(notification: NSNotification) {
            if let keyboardRectValue = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                let keyboardHeight = keyboardRectValue.height
    
                for view in viewsToPushUp {
                    view.frame.origin.y -= keyboardHeight
                }
            }
        }
    }
    
    1. 您还需要正确处理框架,然后才能从中获得正确的行为。

    2. 您应该延长 KeyboardManagerInstance 的生命周期,使其与具有 textField 的 ViewController 的生命周期一样长。您可以按照 Justin 的建议将其声明为 ViewController 中的实例变量。按照您的操作方式,您的 KeyboardManager 实例是一个局部变量,一旦函数超出范围,就会创建并立即释放该变量。要验证这一点,您可以将其添加到 KeyboardManager 类并检查:

        deinit {
          print("KeyboardManager is perhaps dying an untimely death.")
        }
      

    最后你的 ViewController 类应该这样做

      import UIKit
      import FooFramework
    
      class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    var kb: KeyboardManager?
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        kb = KeyboardManager(viewsToPushUp: [textField], notifyFromObject: nil)
       //observe that there is no observer param in the initializer and also no "pushViewsUpWhenKeyboardWillShow" call as that behavior has already been moved to init itself.
    }
    
    }
    

    【讨论】:

    • 谢谢,但我不想在init 中有 addObserver。我计划添加其他几种键盘管理方法,并希望让用户选择他想要使用的那些。
    • 在这种情况下,您所做的似乎是正确的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    • 2018-11-30
    • 1970-01-01
    • 2011-11-09
    • 2022-11-07
    • 1970-01-01
    相关资源
    最近更新 更多