Swift使协议扩展成为通知观察者
我们考虑以下代码:
protocol A { func doA() } extension A { func registerForNotification() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil) } func keyboardDidShow(notification: NSNotification) { } }
现在看一下实现A的UIViewController子类:
class AController: UIViewController, A { override func viewDidLoad() { super.viewDidLoad() self.registerForNotification() triggerKeyboard() } func triggerKeyboard() { // Some code that make key board appear } func doA() { } }
但令人惊讶的是,这会因错误而崩溃:
keyboardDidShow:]:无法识别的选择器发送到实例0x7fc97adc3c60
那么我应该在视图控制器本身中实现观察者吗? 不能留在延期?
以下事情已经尝试过。
制作一个类协议。 将keyboardDidShow添加到协议本身作为签名。
protocol A:class { func doA() func keyboardDidShow(notification: NSNotification) }
我通过实现更新- addObserverForName:object:queue:usingBlock:
NSNotificationCenter
的方法并直接调用方法解决了类似的问题。
extension A where Self: UIViewController { func registerForNotification() { NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil) { [unowned self] notification in self.keyboardDidShow(notification) } } func keyboardDidShow(notification: NSNotification) { print("This will get called in protocol extension.") } }
此示例将导致在协议扩展中调用keyboardDidShow
。
要避免崩溃,请在使用该协议的Swift类中实现observer方法。
实现必须在Swift类本身,而不仅仅是协议扩展,因为选择器总是引用Objective-C方法,并且协议扩展中的函数不能用作Objective-C选择器。 但是,如果Swift类inheritance自Objective-C类,则可以使用Swift类中的方法作为Objective-C选择器
“如果您的Swift类inheritance自Objective-C类,则该类中的所有方法和属性都可用作Objective-C选择器。”
此外,在Xcode 7.1中,当将AnyObject
指定为addObserver
调用中的观察者时,必须将addObserver
给addObserver
。
protocol A { func doA() } extension A { func registerForNotification() { NSNotificationCenter.defaultCenter().addObserver(self as! AnyObject, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil) } func keyboardDidShow(notification: NSNotification) { print("will not appear") } } class ViewController: UIViewController, A { override func viewDidLoad() { super.viewDidLoad() self.registerForNotification() triggerKeyboard() } func triggerKeyboard(){ // Some code that makes the keyboard appear } func doA(){ } func keyboardDidShow(notification: NSNotification) { print("got the notification in the class") } }
在Swift中使用选择器要求您的具体类必须从NSObjectinheritance。 要在协议扩展中强制执行此操作,您应该使用where
。 例如:
protocol A { func doA() } extension A where Self: NSObject { func registerForNotification() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil) } func keyboardDidShow(notification: NSNotification) { } }
除了James Paolantonio的回答。 可以使用关联对象实现unregisterForNotification
方法。
var pointer: UInt8 = 0 extension NSObject { var userInfo: [String: Any] { get { if let userInfo = objc_getAssociatedObject(self, &pointer) as? [String: Any] { return userInfo } self.userInfo = [String: Any]() return self.userInfo } set(newValue) { objc_setAssociatedObject(self, &pointer, newValue, .OBJC_ASSOCIATION_RETAIN) } } } protocol A {} extension A where Self: UIViewController { var defaults: NotificationCenter { get { return NotificationCenter.default } } func keyboardDidShow(notification: Notification) { // Keyboard did show } func registerForNotification() { userInfo["didShowObserver"] = defaults.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil, using: keyboardDidShow) } func unregisterForNotification() { if let didShowObserver = userInfo["didShowObserver"] as? NSObjectProtocol { defaults.removeObserver(didShowObserver, name: .UIKeyboardDidShow, object: nil) } } }
我使用NSObjectProtocol
解决了它,如下所示,
@objc protocol KeyboardNotificaitonDelegate: NSObjectProtocol { func keyboardWillBeShown(notification: NSNotification) func keyboardWillBeHidden(notification: NSNotification) } extension KeyboardNotificaitonDelegate { func registerForKeyboardNotifications() { //Adding notifies on keyboard appearing NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func deregisterFromKeyboardNotifications() { //Removing notifies on keyboard appearing NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) } }
- 如何通过代码判断iPad是黑色还是白色?
- AVCaptureOutput didOutputSampleBuffer停止调用
- 如何在iOS中将自定义元数据写入PNG图像
- 我用什么坐标手动绘制一个MKMapView的自定义path?
- “核心数据不是一个关系型数据库”。为什么这个重要知道?
- 用UIImagePickerController iOS圆形裁剪相机?
- NSNetService工作正常,但不能得到hostName,解决导致错误
- paymentquue updatedTransactions:没有调用,而是paymentQueueRestoreCompletedTransactionsFinished:调用
- NSURLConnection:JSON文本没有以数组或对象和选项开始,以允许片段没有设置