NSNotificationCenter addObserver en Swift

393

¿Cómo agrega un observador en Swift al centro de notificaciones predeterminado? Estoy intentando portar esta línea de código que envía una notificación cuando cambia el nivel de la batería.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
Mora azul
fuente
¿Qué preguntas específicamente? ¿Cómo funciona el selector?
nschum
1
No me di cuenta de que el tipo "Selector" es solo una cadena en Swift. No se menciona en los documentos.
Berry Blue

Respuestas:

443

Es lo mismo que la API Objective-C, pero usa la sintaxis de Swift.

Swift 4.2 y Swift 5:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(self.batteryLevelChanged),
    name: UIDevice.batteryLevelDidChangeNotification,
    object: nil)

Si su observador no hereda de un objeto Objective-C, debe anteponer su método con el @objcfin de usarlo como selector.

@objc private func batteryLevelChanged(notification: NSNotification){     
    //do stuff using the userInfo property of the notification object
}

Consulte la referencia de clase NSNotificationCenter , interactuando con las API de Objective-C

Connor
fuente
3
¡Gracias! No sabía cómo pasar el nombre del selector en Swift.
Berry Blue
14
@BerryBlue, ¿la solución anterior funcionó para usted? Creo que debe cambiar "batteryLevelChanged" a "batteryLevelChanged:" si su función acepta la NSNotification como parámetro.
Olshansk
1
@Olshansk Sí, tienes razón. Necesitas eso. ¡Gracias!
Berry Blue
¿Por qué UIDeviceBatteryLevelDidChangeNotificationno está entre comillas? Es un tipo de cadena.
kmiklas
13
Asegúrese de anotar la clase o el método de destino con @objc.
Klaas
757

Swift 4.0 y Xcode 9.0+:

Enviar (publicar) notificación:

NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)

O

NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil, userInfo: ["Renish":"Dadhaniya"])

Recibir (Obtener) Notificación:

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Manejador de métodos y funciones para notificaciones recibidas:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 y Xcode 8.0+:

Enviar (publicar) notificación:

NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)

Recibir (Obtener) Notificación:

NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Manejador de métodos para notificaciones recibidas:

func methodOfReceivedNotification(notification: Notification) {
  // Take Action on Notification
}

Eliminar notificación:

deinit {
  NotificationCenter.default.removeObserver(self, name: Notification.Name("NotificationIdentifier"), object: nil)
}

Swift 2.3 y Xcode 7:

Enviar (publicar) notificación

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Recibir (Obtener) Notificación

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:"NotificationIdentifier", object: nil)

Manejador de métodos para notificaciones recibidas

func methodOfReceivedNotification(notification: NSNotification){
  // Take Action on Notification
}


Para versiones históricas de Xcode ...



Enviar (publicar) notificación

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Recibir (Obtener) Notificación

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOfReceivedNotification:", name:"NotificationIdentifier", object: nil)

Eliminar notificación

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Remove from all notifications being observed

Manejador de métodos para notificaciones recibidas

func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

Anote la clase o el método de destino con @objc

@objc private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}
Renish Dadhaniya
fuente
21
Asegúrese de anotar la clase o el método de destino con @objc.
Klaas
1
@goofansu ¿Estás seguro? Creo que hay que agregarlo cuando es una clase Swift pura.
Klaas
10
methodOFReceivedNoticationdebe estar anotado dynamico ser miembro de una subclase de NSObject.
Klaas
1
Si no, recibo una advertencia de tiempo de ejecución object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas
2
@TaylorAllred, Muchas gracias por revisar mi respuesta. Realmente aprecio tu sugerencia. Lo he cambiado Por favor, revísalo.
Renish Dadhaniya
46

Una buena manera de hacerlo es usar el addObserver(forName:object:queue:using:)método en lugar del addObserver(_:selector:name:object:)método que se usa a menudo del código Objective-C. La ventaja de la primera variante es que no tiene que usar el @objcatributo en su método:

    func batteryLevelChanged(notification: Notification) {
        // do something useful with this information
    }

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil,
        using: batteryLevelChanged)

e incluso puedes usar un cierre en lugar de un método si quieres:

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil) { _ in print("🔋") }

Puede usar el valor devuelto para dejar de escuchar la notificación más adelante:

    NotificationCenter.default.removeObserver(observer)

Solía ​​haber otra ventaja en el uso de este método, que era que no requería el uso de cadenas de selector que el compilador no podía verificar estáticamente y, por lo tanto, eran frágiles si se cambiaba el nombre del método, pero Swift 2.2 y más tarde incluye #selectorexpresiones que arreglan ese problema.

Jon Colverson
fuente
77
¡Esto es genial! Para completar, me gustaría ver también un ejemplo de cancelación de registro. Es muy diferente a la addObserver(_:selector:name:object:) forma de cancelar el registro. Tienes que mantener el objeto devuelto addObserverForName(_:object:queue:usingBlock:)y pasarlo aremoveObserver:
Lucas Goossen
1
Esto necesita actualizarse para incluir la cancelación del registro de la objeción devuelta por addObserverForName(_:object:queue:usingBlock:).
Hipérbole
3
Esta es una respuesta mucho mejor que la de Connor o Renish (ambas anteriores al momento de este comentario) porque evita tener que usar los métodos de selección de Obj-C. El resultado es mucho más rápido y más correcto, en mi opinión. ¡Gracias!
patr1ck
2
Recuerde, si usa esto en, digamos, ay hace UIViewControllerreferencia selfen ese cierre, debe usarlo [weak self]o tendrá un ciclo de referencia y la pérdida de memoria.
Rob N
40

Swift 3.0 en Xcode 8

Swift 3.0 ha reemplazado muchas API "de tipo secuencial" con struct"tipos de contenedor", como es el caso de NotificationCenter. Las notificaciones ahora se identifican por un en struct Notfication.Namelugar de por String. Consulte la guía Migración a Swift 3 .

Uso previo :

// Define identifier
let notificationIdentifier: String = "NotificationIdentifier"

// Register to receive notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name: notificationIdentifier, object: nil)

// Post a notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)

Nuevo uso de Swift 3.0:

// Define identifier
let notificationName = Notification.Name("NotificationIdentifier")

// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification), name: notificationName, object: nil)

// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)

Todos los tipos de notificación del sistema ahora se definen como constantes estáticas en Notification.Name; es decir .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching,.UITextFieldTextDidChange , etc.

Puede ampliar Notification.Namecon sus propias notificaciones personalizadas para mantenerse coherente con las notificaciones del sistema:

// Definition:
extension Notification.Name {
    static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Jeffrey Fulton
fuente
24
  1. Declarar un nombre de notificación

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Puede agregar observador de dos maneras:

    Utilizando Selector

    NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: .purchaseDidFinish, object: nil)
    
    @objc func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }

    o usando block

    NotificationCenter.default.addObserver(forName: .purchaseDidFinish, object: nil, queue: nil) { [weak self] (notification) in
        guard let strongSelf = self else {
            return
        }
    
        strongSelf.myFunction(notification: notification)
    }
    
    func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }
  3. Publique su notificación

    NotificationCenter.default.post(name: .purchaseDidFinish, object: "myObject", userInfo: ["key": "Value"])

desde iOS 9 y OS X 10.11. Ya no es necesario que un observador de NSNotificationCenter se anule el registro al ser desasignado. más información

Para una blockimplementación basada, necesitas hacer un baile débil-fuerte si quieres usar selfdentro del bloque. más información

Los observadores basados ​​en bloques deben eliminarse más información

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)
Warif Akhand Rishi
fuente
55
"desde iOS 9 y OS X 10.11. Ya no es necesario que un observador de NSNotificationCenter se anule el registro al ser desasignado". Esto es cierto solo para los observadores basados ​​en Selector. Los observadores basados ​​en bloques aún necesitan ser eliminados.
Abhinav
8

Pase datos utilizando NSNotificationCenter

También puede pasar datos utilizando NotificationCentre en swift 3.0 y NSNotificationCenter en swift 2.0.

Versión Swift 2.0

¿Pasar información usando userInfo, que es un diccionario opcional de tipo [NSObject: AnyObject]?

let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
 NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: imageDataDict)

// Register to receive notification in your class
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: notificationName, object: nil)

// handle notification
func showSpinningWheel(notification: NSNotification) {
  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Versión Swift 3.0

El userInfo ahora toma [AnyHashable: Any]? como argumento, que proporcionamos como un diccionario literal en Swift

let imageDataDict:[String: UIImage] = ["image": image]

// post a notification
 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict) 
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// handle notification
func showSpinningWheel(_ notification: NSNotification) {

  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Datos del pase de origen utilizando NotificationCentre (swift 3.0) y NSNotificationCenter (swift 2.0)

Sahil
fuente
Me alegra saber que te ayudó :)
Sahil
6

En Swift 5

Digamos si desea recibir datos de ViewControllerB a ViewControllerA

ViewControllerA (receptor)

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Code for Passing Data through Notification Observer - - - - -
        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: - - - - - Method for receiving Data through Post Notificaiton - - - - -
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllerB (remitente)

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Set data for Passing Data Post Notification - - - - -
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }

}
swiftBoy
fuente
2

Puedo hacer una de las siguientes acciones para usar con éxito un selector, sin anotar nada con @objc:

NSNotificationCenter.defaultCenter().addObserver(self,
    selector:"batteryLevelChanged:" as Selector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

O

let notificationSelector: Selector = "batteryLevelChanged:"

NSNotificationCenter.defaultCenter().addObserver(self,
    selector: notificationSelector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

Mi versión xcrun muestra Swift 1.2, y esto funciona en Xcode 6.4 y Xcode 7 beta 2 (que pensé que usaría Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)
leanne
fuente
No necesita anotar @objcsi su clase de observador hereda de NSObject.
Antonio Favata
Y no debería necesitar explicitar un Stringa Selectortampoco. :)
Antonio Favata
@alfvata: mi clase de observador no hereda de NSObject. Hereda de AnyObject, estilo Swift. Lanzar explícitamente la cadena a Selector me permite evitar hacer cualquiera de las otras soluciones alternativas relacionadas con Objective-C.
leanne
No estoy seguro de entender cómo funciona eso. Eliminé la @objcanotación del método en mi NSObjectclase de no observador, agregué la as Selectortransmisión al Stringnombre del selector y, cuando se activa la notificación, la aplicación se bloquea. Mi versión Swift es exactamente la misma que la tuya.
Antonio Favata
3
@alfavata, no sé qué decirte. Ahora estoy en Xcode Beta 4, y todavía funciona. Mi proyecto es totalmente rápido; No hay componentes de Objective-C. Tal vez eso haga una diferencia. Tal vez hay algo diferente en la configuración del proyecto. ¡Hay muchas posibilidades! Diré: mientras la @objcanotación funcione para ti, y de esta manera no, ¡entonces sigue anotando!
leanne
2

En swift 2.2 - XCode 7.3, utilizamos #selectorparaNSNotificationCenter

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)
Deepak Thakur
fuente
2

Deberíamos eliminar la notificación también.

Ex.

deinit 
{
  NotificationCenter.default.removeObserver(self, name:NSNotification.Name(rawValue: "notify"), object: nil)

}
Pankaj Jangid
fuente
2
Creo que no necesitas esto desde iOS 9. Se hace automáticamente.
Viktor Kucera
1

En swift 3, Xcode 8.2: - comprobación del nivel del estado de la batería

//Add observer
NotificationCenter.default.addObserver(self, selector: #selector(batteryStateDidChange), name: NSNotification.Name.UIDeviceBatteryStateDidChange, object: nil)


 //Fired when battery level changes

 func batteryStateDidChange(notification: NSNotification){
        //perform manipulation here
    }
Dhruv
fuente
1

NSNotificationCenter agrega sintaxis de observador en Swift 4.0 para iOS 11

  NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

Esto es para el tipo de nombre de notificación keyboardWillShow. Se puede seleccionar otro tipo de la opción disponible

el selector es del tipo @objc func que maneja cómo se mostrará el teclado (esta es su función de usuario)

Ashim Dahal
fuente
Solo para aclarar a cualquiera que lea esta respuesta: "el Selector es del tipo @objc func ..." significa que la función asociada con #selectordebe ser anotada @objc. Por ejemplo: ¡ @objc func keyboardShow() { ... }Eso me arrojó por un minuto en Swift 4!
leanne
0

Swift 5 y Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)
David.Chu.ca
fuente
0

Observador de notificaciones de Swift 5

override func viewDidLoad() {
    super.viewDidLoad() 
    NotificationCenter.default.addObserver(self, selector: #selector(batteryLevelChanged), name: UIDevice.batteryLevelDidChangeNotification, object: nil)
}

@objc func batteryLevelChanged(notification : NSNotification){
    //do here code
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: UIDevice.batteryLevelDidChangeNotification, object: nil)

}
Imran Rasheed
fuente