让我们考虑以下代码:
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
那么我应该在视图控制器本身中实现观察者吗?它不能留在扩展中吗?
已经尝试过。
制作A类协议。 将keyboardDidShow添加到协议本身作为签名。
protocol A:class {
func doA()
func keyboardDidShow(notification: NSNotification)
}
答案 0 :(得分:32)
我通过实施- 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
。
答案 1 :(得分:2)
除了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)
}
}
}
答案 2 :(得分:1)
为避免崩溃,请在使用该协议的Swift类中实现observer方法。
实现必须在Swift类本身,而不仅仅是协议扩展,因为选择器总是引用Objective-C方法,并且协议扩展中的函数不能用作Objective-C选择器。如果Swift类继承自Objective-C类,那么来自Swift类的方法可用作Objective-C选择器
此外,在Xcode 7.1中,self
在AnyObject
调用中将其指定为观察者时,必须向下转换为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")
}
}
答案 3 :(得分:1)
在Swift中使用选择器要求您的具体类必须从NSObject继承。要在协议扩展中强制执行此操作,您应使用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) {
}
}
答案 4 :(得分:0)
我使用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)
}
}