如何使用其他类的选择器?

时间:2016-11-27 01:06:09

标签: swift swift3 selector ios10

我有一个名为FooFramework的Cocoa Touch Framework。

在其中,我想管理键盘显示时所选视图在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
            }
        }
    }
}

然后,我在名为Bar的iOS应用程序中导入此FooFramework。为了测试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出现在控制台中,pushViewsUp中的KeyboardManager方法永远不会被调用。尽管我为选择器使用了一个完全限定的名称,但它坚持使用ViewController中的pushViewsUp。这让我疯了。

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

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

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

2 个答案:

答案 0 :(得分:2)

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

此外,您需要在函数范围之外声明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)
}

答案 1 :(得分:1)

除了Justin建议的一切都是正确的之外,在完全解决问题之前还有一些事情需要考虑。

  1. KeyBoardManager类实例本身将观察keyboardWillMoveUp通知,以便您
  2.   

    var observer:任何

    在其中是不必要的。你应该删除它。

    1. 我还将addObserver部分放在KeyBoardManager类本身的init中,以便可以避免这个额外的调用pushViewsUpWhenKeyboardWillShow(),这似乎只是做了什么。由于KeyBoardManager类应该只执行此操作,因此我不明白为什么添加观察者应该是另一个函数调用。
    2. 这就是您的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. 只要具有textField的ViewController处于活动状态,您就应该将KeyboardManagerInstance的生命周期延长到活动状态。你可以通过Justin建议将它声明为ViewController中的实例变量来实现。你这样做的方式,你的KeyboardManager实例是一个局部变量,它会在函数超出范围时立即创建并立即释放。要验证这一点,您可以将其添加到KeyboardManager类并检查:

          deinit {
            print("KeyboardManager is perhaps dying an untimely death.")
          }
        
      3. 最后你的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.
        }
        
        }