423 votes

NSNotificationCenter addObserver en Swift

Comment ajouter un observateur en Swift au centre de notification par défaut? Je essaie de porter cette ligne de code qui envoie une notification lorsque le niveau de la batterie change.

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

0 votes

Que demandez-vous précisément ? Comment fonctionne le sélecteur ?

1 votes

Je n'ai pas réalisé que le type "Selector" est juste une chaîne de caractères en Swift. Aucune mention dans la documentation.

0 votes

788voto

Renish Dadhaniya Points 641

Swift 4.0 & Xcode 9.0+:

Envoyer (Poster) une notification :

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

OU

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

Recevoir une notification :

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

Fonction-Méthode gestionnaire pour la notification reçue :

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

Envoyer (Poster) une notification :

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

Recevoir une notification :

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

Gestionnaire de méthode pour la notification reçue :

func methodOfReceivedNotification(notification: Notification) {
  // Agir sur la notification
}

Supprimer la notification :

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

Swift 2.3 & Xcode 7:

Envoyer (Poster) une notification

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

Recevoir une notification

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

Gestionnaire de méthode pour la notification reçue

func methodOfReceivedNotification(notification: NSNotification){
  // Agir sur la notification
}


Pour les anciennes versions de Xcode...



Envoyer (Poster) une notification

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

Recevoir une notification

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

Supprimer la notification

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Supprimer toutes les notifications observées

Gestionnaire de méthode pour la notification reçue

func methodOfReceivedNotification(notification: NSNotification) {
  // Agir sur la notification
}

Annotation de la classe ou de la méthode cible avec @objc

@objc private func methodOfReceivedNotification(notification: NSNotification) {
  // Agir sur la notification
}

// Ou

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Agir sur la notification
}

21 votes

Assurez-vous d'annoter soit la classe soit la méthode cible avec @objc.

0 votes

@Klaas Pas besoin d'ajouter @objc

1 votes

@goofansu Es-tu sûr? Je pense que tu dois l'ajouter quand c'est une classe purement Swift.

467voto

connor Points 9825

C'est la même chose que l'API Objective-C, mais utilise la syntaxe de Swift.

Swift 4.2 & Swift 5:

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

Si votre observateur n'hérite pas d'un objet Objective-C, vous devez préfixer votre méthode avec @objc pour l'utiliser comme sélecteur.

@objc private func batteryLevelChanged(notification: NSNotification){     
    // faire quelque chose en utilisant la propriété userInfo de l'objet notification
}

Voir Référence de la classe NSNotificationCenter, Interaction avec les API Objective-C

14 votes

@BerryBlue, est-ce que la solution ci-dessus a fonctionné pour vous ? Je crois que vous devez changer "batteryLevelChanged" en "batteryLevelChanged:" si votre fonction accepte NSNotification en tant que paramètre.

0 votes

Pourquoi UIDeviceBatteryLevelDidChangeNotification n'est pas entre guillemets? C'est de type string.

0 votes

@kmiklas Vous pouvez le mettre entre guillemets si vous le souhaitez. UIDeviceBatteryLevelDidChangeNotification est une chaîne de caractères de "UIDeviceBatteryLevelDidChangeNotification". De toute façon, vous obtenez le même résultat.

49voto

Jon Colverson Points 984

Une bonne façon de faire cela est d'utiliser la méthode addObserver(forName:object:queue:using:) plutôt que la méthode addObserver(_:selector:name:object:) qui est souvent utilisée à partir du code Objective-C. L'avantage de la première variante est que vous n'avez pas besoin d'utiliser l'attribut @objc sur votre méthode :

    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)

et vous pouvez même simplement utiliser une fermeture au lieu d'une méthode si vous le souhaitez :

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

Vous pouvez utiliser la valeur retournée pour cesser d'écouter la notification plus tard :

    NotificationCenter.default.removeObserver(observer)

Il y avait un autre avantage à utiliser cette méthode, qui était qu'elle ne nécessitait pas l'utilisation de chaînes de sélecteurs qui ne pouvaient pas être vérifiées statiquement par le compilateur et étaient donc fragiles en cas de renommage de la méthode, mais Swift 2.2 et ultérieur incluent des #selector expressions qui résolvent ce problème.

7 votes

C'est génial! Pour être complet, j'aimerais juste voir un exemple de désinscription aussi. C'est assez différent de la manière de désinscription addObserver(_:selector:name:object:). Vous devez conserver l'objet renvoyé par addObserverForName(_:object:queue:usingBlock:) et le passer à removeObserver:

1 votes

Cela doit être mis à jour pour inclure la désinscription de l'objet renvoyé par addObserverForName(_:object:queue:usingBlock:).

4 votes

C'est une réponse beaucoup meilleure que celle de Connor ou Renish (tous deux ci-dessus au moment de ce commentaire) car elle échappe à l'utilisation des méthodes #selector Obj-C. Le résultat est beaucoup plus Swift-y et plus correct, à mon avis. Merci!

41voto

Jeffrey Fulton Points 3910

Swift 3.0 dans Xcode 8

Swift 3.0 a remplacé de nombreuses API "stringly-typed" par des types "wrapper" struct, comme c'est le cas avec NotificationCenter. Les notifications sont maintenant identifiées par un struct Notfication.Name plutôt que par une String. Voir le guide de migration vers Swift 3.

Utilisation précédente :

// Définir l'identifiant
let notificationIdentifier: String = "NotificationIdentifier"

// S'inscrire pour recevoir la notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(NomDeVotreClasse.methodeDeReceptionDeLaNotif(_:)), name: notificationIdentifier, object: nil)

// Poster une notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)

Nouvelle utilisation Swift 3.0 :

// Définir l'identifiant
let notificationName = Notification.Name("NotificationIdentifier")

// S'inscrire pour recevoir la notification
NotificationCenter.default.addObserver(self, selector: #selector(NomDeVotreClasse.methodeDeReceptionDeLaNotif), name: notificationName, object: nil)

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

Tous les types de notification système sont désormais définis en tant que constantes statiques sur Notification.Name; par exemple .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, etc.

Vous pouvez étendre Notification.Name avec vos propres notifications personnalisées afin de rester cohérent avec les notifications système :

// Définition :
extension Notification.Name {
    static let votreNomDeNotificationPersonnalise = Notification.Name("votreNomDeNotificationPersonnalise")
}

// Utilisation :
NotificationCenter.default.post(name: .votreNomDeNotificationPersonnalise, object: nil)

40voto

Warif Akhand Rishi Points 3407
  1. Déclarez un nom de notification

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Vous pouvez ajouter un observateur de deux manières:

    En utilisant 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"]
    }

    ou en utilisant un 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. Publiez votre notification

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

à partir d'iOS 9 et OS X 10.11. Il n'est plus nécessaire pour un observateur de NSNotificationCenter de se désinscrire lors de sa désallocation. plus d'informations

Pour une implémentation basée sur un block, vous devez faire une danse faible-forte si vous voulez utiliser self à l'intérieur du bloc. plus d'informations

Les observateurs basés sur des blocs doivent être supprimés plus d'informations

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

6 votes

"à partir d'iOS 9 et d'OS X 10.11. Il n'est plus nécessaire pour un observateur NSNotificationCenter de se désinscrire lorsqu'il est désalloué." Ceci est vrai uniquement pour les observateurs basés sur les Sélecteurs. Les observateurs basés sur les Blocs doivent encore être supprimés.

0 votes

Vous n'avez pas besoin de faire la danse faible-forte s'il n'y a qu'une seule ligne de code dans le bloc. Vous pouvez simplement utiliser le weak comme self?.myFunction. Eh bien, c'était le cas en ObjC, je suppose que c'est la même chose en Swift.

0 votes

@Abhinav comment se fait-il qu'ils aient de telles différences? C'est assez étrange.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X