ios - extensions - swift protocol and delegate example
Swift hace que la extensión de protocolo sea un observador de notificación (5)
Además de la respuesta de James Paolantonio. Un método unregisterForNotification
se puede implementar utilizando objetos asociados.
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)
}
}
}
Consideremos el siguiente código:
protocol A {
func doA()
}
extension A {
func registerForNotification() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil)
}
func keyboardDidShow(notification: NSNotification) {
}
}
Ahora mira una subclase UIViewController que implementa A:
class AController: UIViewController, A {
override func viewDidLoad() {
super.viewDidLoad()
self.registerForNotification()
triggerKeyboard()
}
func triggerKeyboard() {
// Some code that make key board appear
}
func doA() {
}
}
Pero sorprendentemente esto falla con un error:
keyboardDidShow:]: selector no reconocido enviado a la instancia 0x7fc97adc3c60
Entonces, ¿debería implementar el observador en el controlador de vista en sí? ¿No puede quedarse en la extensión?
Siguiendo cosas ya probadas.
haciendo que A sea un protocolo de clase. Añadiendo keyboardDidShow al protocolo en sí mismo como firma.
protocol A:class {
func doA()
func keyboardDidShow(notification: NSNotification)
}
El uso de selectores en Swift requiere que su clase concreta debe heredar de NSObject. Para aplicar esto en una extensión de protocolo, debe usar where
. Por ejemplo:
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) {
}
}
Lo resolví usando NSObjectProtocol
como abajo,
@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)
}
}
Para evitar el bloqueo, implemente el método del observador en la clase Swift que usa el protocolo.
La implementación debe estar en la clase Swift en sí, no solo en la extensión de protocolo, porque un selector siempre se refiere a un método Objective-C, y una función dentro de una extensión de protocolo no está disponible como un selector Objective-C. Sin embargo, los métodos de una clase Swift están disponibles como selectores Objective-C si la clase Swift hereda de una clase Objective-C
Además, en Xcode 7.1, el self
tiene que ser AnyObject
a AnyObject
cuando se lo especifica como el observador en la llamada 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")
}
}
- addObserverForName:object:queue:usingBlock:
un problema similar implementando el - addObserverForName:object:queue:usingBlock:
más nuevo - addObserverForName:object:queue:usingBlock:
de NSNotificationCenter
y llamando directamente al método.
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.")
}
}
Este ejemplo provocará que se llame a keyboardDidShow
en la extensión de protocolo.