2015-10-08 7 views
19

Рассмотрим следующий код:расширение Swift протокол, уведомляет наблюдатель

protocol A { 
    func doA() 
} 

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

    func keyboardDidShow(notification: NSNotification) { 

    } 
} 

Теперь рассмотрим подкласс 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:]: unrecognized selector sent to instance 0x7fc97adc3c60

Должен ли я реализовать наблюдателя в самом контроллере представления? Не может ли он остаться в продлении?

Следующие вещи уже пробовали.

Создание протокола класса. Добавление клавиатурыDidShow к самому протоколу в качестве подписи.

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

Я пытался что-то подобное тоже в прошлом, но я обнаружил, что расширения протокола Свифта не работают с протоколами Objective-C и классы [но, по-видимому, они как-то делают] (http://stackoverflow.com/questions/27097688/can-objective-c-code-call-swift-extension-on-class), я смущен – Kametrixom

+0

'extension A { } '??? Вы говорите о 'extension Controller {}' –

+0

Вам просто нужно добавить параметр к методу или удалить: с конца имени селектора –

ответ

1

Чтобы избежать сбоя, применяйте метод наблюдателя в классе Swift, который использует протокол.

Реализация должна быть в классе Swift, а не только в расширении протокола, поскольку селектор всегда ссылается на метод Objective-C, а функция внутри расширения протокола недоступна в качестве селектора Objective-C. Тем не менее, методы из Swift класса доступны в качестве селекторов Objective-C, если Свифт класс наследует от класса Objective-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.”

Кроме того, в 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") 
    } 
} 
28

Я решил подобную проблему путем реализации более новый - 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 в расширении протокола.

+0

так очевиден, но не думал об этом - cudos! – manmal

+0

Отличная находка! Пропущено это :) –

+3

Как удалить наблюдателя? допустим, у меня есть функция, которая удаляет наблюдателя (также в расширении), а затем просто вызывает это из deinit – tolkiana

1

Использование селекторов в Swift требует, чтобы ваш конкретный класс наследовал от NSObject. Чтобы обеспечить это в расширении протокола, вы должны использовать where. Например:

1

В дополнение к ответу Джеймса Паолантонио. Метод 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) 
     } 
    } 
} 
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) 
} 
} 
Смежные вопросы