如何使用另一个 class 的选择器?
How to use a selector from another class?
我有一个名为 FooFramework 的 Cocoa 触控框架。
在其中,我想管理键盘显示时所选视图在 Y 轴上的向上移动。我创建了一个 KeyboardManager
class。外观如下:
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
?
我认为您需要在 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)
}
除了 Justin 提出的完全正确的建议之外,在您完全解决问题之前还有一些事情需要考虑。
- KeyBoardManager class 实例本身将观察 keyboardWillMoveUp 通知,因此您的
var observer: Any
里面没有必要。你应该删除它。
- 我还会将 addObserver 部分放在 KeyBoardManager class 本身的初始化中,这样就可以避免这个额外的调用 pushViewsUpWhenKeyboardWillShow() ,它似乎只做那个。由于 KeyBoardManager class 应该只做这个,我不明白为什么添加观察者应该是另一个函数调用。
这就是您的 KeyboardManager class 的外观:
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
}
}
}
}
在获得正确的行为之前,您还需要正确使用框架。
您应该延长 KeyboardManagerInstance 的生命周期,使其与具有 textField 的 ViewController 存在的时间一样长。正如贾斯汀所建议的那样,您可以通过将其声明为 ViewController 内的实例变量来实现。你这样做的方式,你的 KeyboardManager 实例是一个局部变量,一旦函数超出范围就会创建并立即释放。要验证这一点,您可以将其添加到您的 KeyboardManager class 并检查:
deinit {
print("KeyboardManager is perhaps dying an untimely death.")
}
最后你的 ViewController class 应该做到这一点
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.
}
}
我有一个名为 FooFramework 的 Cocoa 触控框架。
在其中,我想管理键盘显示时所选视图在 Y 轴上的向上移动。我创建了一个 KeyboardManager
class。外观如下:
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
?
我认为您需要在 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)
}
除了 Justin 提出的完全正确的建议之外,在您完全解决问题之前还有一些事情需要考虑。
- KeyBoardManager class 实例本身将观察 keyboardWillMoveUp 通知,因此您的
var observer: Any
里面没有必要。你应该删除它。
- 我还会将 addObserver 部分放在 KeyBoardManager class 本身的初始化中,这样就可以避免这个额外的调用 pushViewsUpWhenKeyboardWillShow() ,它似乎只做那个。由于 KeyBoardManager class 应该只做这个,我不明白为什么添加观察者应该是另一个函数调用。
这就是您的 KeyboardManager class 的外观:
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
}
}
}
}
在获得正确的行为之前,您还需要正确使用框架。
您应该延长 KeyboardManagerInstance 的生命周期,使其与具有 textField 的 ViewController 存在的时间一样长。正如贾斯汀所建议的那样,您可以通过将其声明为 ViewController 内的实例变量来实现。你这样做的方式,你的 KeyboardManager 实例是一个局部变量,一旦函数超出范围就会创建并立即释放。要验证这一点,您可以将其添加到您的 KeyboardManager class 并检查:
deinit { print("KeyboardManager is perhaps dying an untimely death.") }
最后你的 ViewController class 应该做到这一点
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.
}
}