NSNotificationCenter addObserver in Swift


393

Come si aggiunge un osservatore in Swift al centro di notifica predefinito? Sto provando a portare questa riga di codice che invia una notifica quando il livello della batteria cambia.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];

Cosa stai chiedendo specificamente? Come funziona il selettore?
nschum,

1
Non mi ero reso conto che il tipo "Selector" fosse solo una stringa in Swift. Nessuna menzione nei documenti.
Berry Blue

Risposte:


443

È uguale all'API Objective-C, ma utilizza la sintassi di Swift.

Swift 4.2 e Swift 5:

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

Se il tuo osservatore non eredita da un oggetto Objective-C, devi @objcusare il prefisso del tuo metodo per usarlo come selettore.

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

Vedere Riferimento alla classe NSNotificationCenter , Interazione con le API Objective-C


3
Grazie! Non sapevo come passare il nome del selettore in Swift.
Berry Blue

14
@BerryBlue, la soluzione sopra ha funzionato per te? Credo che sia necessario modificare "batteryLevelChanged" in "batteryLevelChanged:" se la funzione accetta NSNotification come parametro.
Olshansk,

1
@Olshansk Sì, hai ragione. Ne hai bisogno. Grazie!
Berry Blue,

perché UIDeviceBatteryLevelDidChangeNotificationnon è tra virgolette? È un tipo di stringa.
kmiklas,

13
Assicurati di annotare la classe o il metodo target con @objc.
Klaas,

757

Swift 4.0 e Xcode 9.0+:

Invia notifica (posta):

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

O

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

Ricevi (ricevi) notifica:

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

Gestore del metodo di funzione per la notifica ricevuta:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 e Xcode 8.0+:

Invia notifica (posta):

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

Ricevi (ricevi) notifica:

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

Gestore del metodo per la notifica ricevuta:

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

Rimuovi notifica:

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

Swift 2.3 e Xcode 7:

Invia notifica (posta)

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

Ricevi (ricevi) notifica

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

Gestore del metodo per la notifica ricevuta

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


Per le versioni storiche di Xcode ...



Invia notifica (posta)

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

Ricevi (ricevi) notifica

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

Rimuovi notifica

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

Gestore del metodo per la notifica ricevuta

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

Annota la classe o il metodo target con @objc

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

// Or

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

21
Assicurati di annotare la classe o il metodo target con @objc.
Klaas,

1
@goofansu Sei sicuro? Suppongo che devi aggiungerlo quando è una pura classe Swift.
Klaas,

10
methodOFReceivedNoticationdeve essere annotato con dynamico essere membro di una sottoclasse di NSObject.
Klaas,

1
In caso contrario, ricevo un avviso di runtime object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas il

2
@TaylorAllred, Grazie mille per rivedere la mia risposta. Apprezzo molto il tuo suggerimento. L'ho cambiato Si prega di rivederlo.
Renish Dadhaniya, il

46

Un buon modo per farlo è usare il addObserver(forName:object:queue:using:)metodo piuttosto che il addObserver(_:selector:name:object:)metodo che viene spesso usato dal codice Objective-C. Il vantaggio della prima variante è che non è necessario utilizzare l' @objcattributo sul metodo:

    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 puoi anche usare una chiusura anziché un metodo se vuoi:

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

È possibile utilizzare il valore restituito per interrompere l'ascolto della notifica in un secondo momento:

    NotificationCenter.default.removeObserver(observer)

C'era un altro vantaggio nell'uso di questo metodo, che consisteva nel fatto che non richiedeva l'uso di stringhe di selezione che non potevano essere controllate staticamente dal compilatore e quindi fragili da rompersi se il metodo è stato rinominato, ma Swift 2.2 e in seguito includono #selectorespressioni che risolvono quel problema.


7
Questo è fantastico! Per completezza, vorrei solo vedere un esempio non registrato. È abbastanza diverso dal addObserver(_:selector:name:object:) modo di annullare la registrazione. Devi mantenere l'oggetto restituito addObserverForName(_:object:queue:usingBlock:)e passarlo aremoveObserver:
Lucas Goossen il

1
Questo deve essere aggiornato per includere la cancellazione dell'oggetto restituito da addObserverForName(_:object:queue:usingBlock:).
Iperbole

3
Questa è una risposta molto migliore di quella di Connor o Renish (entrambe sopra al momento di questo commento) perché aggira il dovere di usare i metodi Obj-C #selector. Il risultato è molto più veloce e più corretto, IMO. Grazie!
patr1ck,

2
Ricorda, se lo usi in, diciamo, UIViewControllere ti riferisci a selfquella chiusura, devi usare [weak self]o avrai un ciclo di riferimento e la perdita di memoria.
Rob N,

40

Swift 3.0 in Xcode 8

Swift 3.0 ha sostituito molte API "tipicamente stringa" con struct"tipi di wrapper", come nel caso di NotificationCenter. Le notifiche sono ora identificate da un struct Notfication.Nameanziché da String. Consulta la guida alla migrazione a Swift 3 .

Utilizzo precedente :

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

Nuovo utilizzo di 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)

Tutti i tipi di notifica del sistema sono ora definiti come costanti statiche attive Notification.Name; cioè .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching,.UITextFieldTextDidChange , etc.

Puoi estenderti Notification.Namecon le tue notifiche personalizzate per rimanere coerente con le notifiche di sistema:

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)

24
  1. Dichiara un nome di notifica

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Puoi aggiungere l'osservatore in due modi:

    utilizzando 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. Pubblica la tua notifica

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

da iOS 9 e OS X 10.11. Non è più necessario che un osservatore NSNotificationCenter si annulli la sua registrazione quando viene deallocato. Ulteriori informazioni

Per blockun'implementazione basata è necessario eseguire una danza debole e forte se si desidera utilizzare selfall'interno del blocco. Ulteriori informazioni

È necessario rimuovere più informazioni dagli osservatori basati su blocchi

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)

5
"da iOS 9 e OS X 10.11. Non è più necessario che un osservatore NSNotificationCenter si annulli la sua registrazione quando viene deallocato." Questo è vero solo per gli osservatori basati su Selector. Gli osservatori basati su blocchi devono ancora essere rimossi.
Abhinav,

8

Passare i dati utilizzando NSNotificationCenter

Puoi anche passare i dati utilizzando NotificationCentre in swift 3.0 e NSNotificationCenter in swift 2.0.

Versione Swift 2.0

Passare informazioni utilizzando userInfo che è un dizionario facoltativo di 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   
  }
}

Versione Swift 3.0

L'utenteInfo ora prende [AnyHashable: Any]? come argomento, che forniamo come dizionario letterale in 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   
  }
}

Dati di passaggio sorgente utilizzando NotificationCentre (swift 3.0) e NSNotificationCenter (swift 2.0)


Sono contento di sentirti aiutato :)
Sahil,

6

In Swift 5

Diciamo se si desidera ricevere dati da ViewControllerB a ViewControllerA

ViewControllerA (Ricevitore)

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 (mittente)

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

}

2

Sono in grado di eseguire una delle seguenti operazioni per utilizzare correttamente un selettore, senza annotare nulla 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)    

La mia versione di xcrun mostra Swift 1.2, e questo funziona su Xcode 6.4 e Xcode 7 beta 2 (che pensavo avrebbe usato Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)

Non è necessario annotare @objcse la tua classe di osservatori eredita da NSObject.
Antonio Favata,

E non dovresti aver bisogno di lanciare esplicitamente Stringa Selector. :)
Antonio Favata,

@alfvata: la mia classe di osservatori non eredita da NSObject. Eredita da AnyObject, in stile Swift. Il cast esplicito della stringa su Selector mi sta permettendo di evitare qualsiasi altra soluzione correlata a Objective-C.
leanne,

Non sono sicuro di capire come funziona. Ho rimosso l' @objcannotazione dal metodo nella mia NSObjectclasse non osservatore, ho aggiunto il as Selectorcasting al Stringnome del selettore e quando la notifica si attiva, l'app si blocca. La mia versione di Swift è esattamente uguale alla tua.
Antonio Favata,

3
@alfavata, non so cosa dirti. Ora sono su Xcode Beta 4 e funziona ancora. Il mio progetto è totalmente rapido; non ci sono componenti Objective-C. Forse questo fa la differenza. Forse c'è qualcosa di diverso nelle impostazioni del progetto. Ci sono molte possibilità! Dirò: finché l' @objcannotazione funziona per te, e in questo modo no, quindi continua ad annotare!
leanne,

2

In rapido 2.2 - XCode 7.3, usiamo #selectorperNSNotificationCenter

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

2

Dovremmo rimuovere anche la notifica.

Ex.

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

}

2
Credo che non ti serva da iOS 9. È fatto automaticamente.
Viktor Kucera,

1

In swift 3, Xcode 8.2: - verifica del livello di stato della batteria

//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
    }

1

NSNotificationCenter aggiunge la sintassi dell'osservatore in Swift 4.0 per iOS 11

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

Questo è per il tipo di nome di notifica keyboardWillShow. Altro tipo può essere selezionato dall'opzione disponibile

il Selettore è di tipo @objc func che gestisce la modalità di visualizzazione della tastiera (questa è la tua funzione utente)


Giusto per chiarire per chiunque legga questa risposta: "il Selettore è di tipo @objc func ..." significa che la funzione associata #selectordeve essere annotata con @objc. Ad esempio: @objc func keyboardShow() { ... }mi ha gettato un minuto in Swift 4!
leanne,

0

Swift 5 e Xcode 10.2:

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

0

Swift 5 Notification Observer

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)

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.