2015-10-08 14 views
19

Rozważmy następujący kod:Swift rozszerzenie protokołu dokonać obserwator Powiadomienie

protocol A { 
    func doA() 
} 

extension A { 
    func registerForNotification() { 
     NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil) 
    } 

    func keyboardDidShow(notification: NSNotification) { 

    } 
} 

Spójrzmy teraz na podklasy UIViewController, który implementuje:

class AController: UIViewController, A { 
    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.registerForNotification() 
     triggerKeyboard() 
    } 

    func triggerKeyboard() { 
     // Some code that make key board appear 
    } 

    func doA() { 
    } 
} 

Ale zaskakująco to wywala się z błędem:

keyboardDidShow:]: unrecognized selector sent to instance 0x7fc97adc3c60

Czy powinienem zatem wdrożyć obserwatora w kontroler widoku? Czy nie może pozostać w rozszerzeniu?

Następujące rzeczy już wypróbowane.

Tworzenie protokołu klasy. Dodawanie keyboardDidShow do samego protokołu jako podpisu.

protocol A:class { 
    func doA() 
    func keyboardDidShow(notification: NSNotification) 
} 
+0

próbowałam coś podobnego też w przeszłości, ale okazało się, że rozszerzenia protokołu Swifta nie działają z protokołami Objective-C i klasy, [ale najwyraźniej jakoś to robią] (http://stackoverflow.com/questions/27097688/can-objective-c-code-call-swift-extension-on-class), jestem zdezorientowany – Kametrixom

+0

'extension A { } '??? Czy mówisz o 'rozszerzeniu Controller {}' –

+0

Po prostu musisz dodać parametr do metody lub usunąć: od końca nazwy selektora o nazwie –

Odpowiedz

1

Aby uniknąć awarii, zaimplementuj metodę obserwatora w klasie Swift, która używa protokołu.

Implementacja musi być w samej klasie Swift, a nie tylko w rozszerzeniu protokołu, ponieważ selektor zawsze odnosi się do metody Objective-C, a funkcja w rozszerzeniu protokołu nie jest dostępna jako selektor Objective-C. Jednak metody z szybkiej klasy są dostępne jako przełączniki celu C jeśli Swift klasa dziedziczy klasy Cel C

“If your Swift class inherits from an Objective-C class, all of the methods and properties in the class are available as Objective-C selectors.”

Również w Xcode 7.1 self musi być przybity do AnyObject, określając go jako obserwatora w wywołaniu 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") 
    } 
} 
28

I rozwiązać podobny problem poprzez wprowadzenie nowszej - addObserverForName:object:queue:usingBlock: sposób NSNotificationCenter i wywołanie metody bezpośrednio.

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.") 
    } 
} 

Ten przykład spowoduje wywołanie keyboardDidShow w rozszerzeniu protokołu.

+0

tak oczywiste, ale nie myślałem o tym - cudos! – manmal

+0

Świetne znalezisko! Tęskniłem za tym :) –

+3

Jak usunąć obserwatora? załóżmy, że chcę mieć funkcję, która usuwa obserwatora (również w rozszerzeniu), a następnie po prostu wywołaj to z deinit – tolkiana

1

Używanie selektorów w Swift wymaga, aby twoja klasa betonu dziedziczyła po NSObject. Aby wymusić to w rozszerzeniu protokołu, należy użyć where. Na przykład:

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) { 

    } 
} 
1

Oprócz odpowiedzi James Paolantonio. Metoda unregisterForNotification może zostać zaimplementowana przy użyciu powiązanych obiektów.

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) 
     } 
    } 
} 
0

I rozwiązać go za pomocą NSObjectProtocol jak poniżej,

@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) 
} 
} 
Powiązane problemy