NSNotificationCenter addObserver dans Swift

393

Comment ajouter un observateur dans Swift au centre de notification par défaut? J'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];
Bleu baie
la source
Que demandez-vous précisément? Comment fonctionne le sélecteur?
nschum
1
Je ne savais pas que le type "Selector" n'était qu'une chaîne dans Swift. Aucune mention dans les documents.
Berry Blue

Réponses:

443

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

Swift 4.2 et 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 @objcafin de l'utiliser comme sélecteur.

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

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

Connor
la source
3
Merci! Je ne savais pas comment passer le nom du sélecteur dans Swift.
Berry Blue
14
@BerryBlue, la solution ci-dessus a-t-elle fonctionné pour vous? Je crois que vous devez changer "batteryLevelChanged" en "batteryLevelChanged:" si votre fonction accepte la NSNotification comme paramètre.
Olshansk
1
@Olshansk Oui, vous avez raison. Vous en avez besoin. Merci!
Berry Blue
pourquoi n'est UIDeviceBatteryLevelDidChangeNotificationpas entre guillemets? C'est un type de chaîne.
kmiklas
13
Assurez-vous d'annoter la classe ou la méthode cible avec @objc.
Klaas
757

Swift 4.0 et Xcode 9.0+:

Envoyer une notification (post):

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

OU

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

Recevoir (obtenir) une notification:

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

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

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 et Xcode 8.0+:

Envoyer une notification (post):

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

Recevoir (obtenir) 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) {
  // Take Action on Notification
}

Supprimer la notification:

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

Swift 2.3 et Xcode 7:

Envoyer une notification (post)

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

Recevoir (obtenir) 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){
  // Take Action on Notification
}


Pour les versions Xcode historiques ...



Envoyer une notification (post)

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

Recevoir (obtenir) 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) // Remove from all notifications being observed

Gestionnaire de méthode pour la notification reçue

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

Annotez la classe ou la méthode cible avec @objc

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

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}
Renish Dadhaniya
la source
21
Assurez-vous d'annoter la classe ou la méthode cible avec @objc.
Klaas
1
@goofansu Êtes-vous sûr? Je pense que vous devez l'ajouter quand c'est une pure classe Swift.
Klaas
10
methodOFReceivedNoticationdoit être annoté avec dynamicou être membre d'une sous-classe de NSObject.
Klaas
1
Sinon, je reçois un avertissement d'exécution object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas
2
@TaylorAllred, Merci beaucoup d'avoir revu ma réponse. J'apprécie vraiment votre suggestion. Je l'ai changé. Veuillez l'examiner.
Renish Dadhaniya
46

Une bonne façon de le faire est d'utiliser la addObserver(forName:object:queue:using:)méthode plutôt que la addObserver(_:selector:name:object:)méthode qui est souvent utilisée à partir du code Objective-C. L'avantage de la première variante est que vous n'avez pas à utiliser l' @objcattribut 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 renvoyée pour arrêter d'écouter la notification ultérieurement:

    NotificationCenter.default.removeObserver(observer)

Il y avait un autre avantage à utiliser cette méthode, à savoir qu'elle ne vous oblige pas à utiliser des chaînes de sélection qui ne pouvaient pas être vérifiées statiquement par le compilateur et étaient donc fragiles à casser si la méthode était renommée, mais Swift 2.2 et inclure plus tard des #selectorexpressions qui résolvent ce problème.

Jon Colverson
la source
7
C'est bien! Pour être complet, je voudrais juste voir un exemple de non-enregistrement aussi. C'est tout à fait différent de la addObserver(_:selector:name:object:) façon de se désinscrire. Vous devez garder l'objet retourné addObserverForName(_:object:queue:usingBlock:)et le passer àremoveObserver:
Lucas Goossen
1
Cela doit être mis à jour pour inclure le désenregistrement de l'objet retourné par addObserverForName(_:object:queue:usingBlock:).
Hyperbole
3
C'est une bien meilleure réponse que celle de connor ou de Renish (toutes deux ci-dessus au moment de ce commentaire) car elle contourne la nécessité d'utiliser les méthodes de sélection d'obj-C #. Le résultat est beaucoup plus Swift-y et plus correct, IMO. Merci!
patr1ck
2
N'oubliez pas que si vous l'utilisez dans, disons, a UIViewControlleret selffaites référence à cette fermeture, vous devez utiliser [weak self]ou vous aurez un cycle de référence et la fuite de mémoire.
Rob N
40

Swift 3.0 dans Xcode 8

Swift 3.0 a remplacé de nombreuses API "typées en chaîne" par des struct"types d'encapsuleur", comme c'est le cas avec NotificationCenter. Les notifications sont désormais identifiées par un struct Notfication.Nameplutôt que par String. Consultez le guide Migration vers Swift 3 .

Utilisation précédente :

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

Nouvelle utilisation 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)

Tous les types de notification système sont désormais définis comme des constantes statiques sur Notification.Name; à savoir .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, etc.

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

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Jeffrey Fulton
la source
24
  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 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. Postez votre notification

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

depuis iOS 9 et OS X 10.11. Il n'est plus nécessaire pour un observateur NSNotificationCenter de se désinscrire lorsqu'il est désalloué.Plus d'informations

Pour une blockimplémentation basée, vous devez faire une danse faible-forte si vous voulez utiliserself à 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)
Warif Akhand Rishi
la source
5
"depuis iOS 9 et OS X 10.11. Il n'est plus nécessaire pour un observateur NSNotificationCenter de se désinscrire lorsqu'il est désalloué." Cela n'est vrai que pour les observateurs basés sur Selector. Les observateurs basés sur des blocs doivent encore être supprimés.
Abhinav
8

Passer des données à l'aide de NSNotificationCenter

Vous pouvez également transmettre des données à l'aide de NotificationCentre dans swift 3.0 et NSNotificationCenter dans swift 2.0.

Version Swift 2.0

Passer des informations en utilisant userInfo qui est un dictionnaire facultatif de type [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   
  }
}

Version Swift 3.0

L'utilisateur info prend maintenant [AnyHashable: Any]? comme argument, que nous fournissons comme un littéral de dictionnaire dans 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   
  }
}

Données de passe source à l'aide de NotificationCentre (swift 3.0) et NSNotificationCenter (swift 2.0)

Sahil
la source
Heureux d'apprendre que cela vous a aidé :)
Sahil
6

Dans Swift 5

Disons que si vous souhaitez recevoir des données de ViewControllerB vers ViewControllerA

ViewControllerA (récepteur)

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 (expéditeur)

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
la source
2

Je peux faire l'une des choses suivantes pour utiliser avec succès un sélecteur - sans rien annoter avec @objc:

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

OU

let notificationSelector: Selector = "batteryLevelChanged:"

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

Ma version xcrun montre Swift 1.2, et cela fonctionne sur Xcode 6.4 et Xcode 7 beta 2 (que je pensais utiliser Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)
leanne
la source
Vous n'avez pas besoin d'annoter @objcsi votre classe d'observateur hérite de NSObject.
Antonio Favata
Et vous ne devriez pas non plus avoir à transposer explicitement un Stringvers Selector. :)
Antonio Favata
@alfvata: Ma classe d'observateur n'hérite pas de NSObject. Il hérite de AnyObject, style Swift. Le fait de transposer explicitement la chaîne dans Selector me permet d'éviter toute autre solution de contournement liée à Objective-C.
leanne
Je ne suis pas sûr de comprendre comment cela fonctionne. J'ai supprimé l' @objcannotation de la méthode dans ma NSObjectclasse non- observateur, ajouté le as Selectorcasting au Stringnom du sélecteur et lorsque la notification se déclenche, l'application se bloque. Ma version Swift est exactement la même que la vôtre.
Antonio Favata
3
@alfavata, je ne sais pas quoi vous dire. Je suis maintenant sur Xcode Beta 4, et cela fonctionne toujours. Mon projet est totalement Swift; il n'y a pas de composants Objective-C. Peut-être que cela fait une différence. Il y a peut-être quelque chose de différent dans les paramètres du projet. Il existe de nombreuses possibilités! Je dirai: tant que l' @objcannotation fonctionne pour vous, et pas de cette façon, alors continuez à annoter!
leanne
2

Dans swift 2.2 - XCode 7.3, nous utilisons #selectorpourNSNotificationCenter

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

Nous devons également supprimer la notification.

Ex.

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

}
Pankaj Jangid
la source
2
Je pense que vous n'en avez pas besoin depuis iOS 9. Cela se fait automatiquement.
Viktor Kucera
1

Dans swift 3, Xcode 8.2: - vérification du niveau d'état de la batterie

//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
la source
1

NSNotificationCenter ajoute une syntaxe d'observateur dans Swift 4.0 pour iOS 11

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

Il s'agit du type de nom de notification keyboardWillShow. Un autre type peut être sélectionné parmi les options disponibles

le sélecteur est de type @objc func qui gère la façon dont le clavier s'affichera (c'est votre fonction utilisateur)

Ashim Dahal
la source
Juste pour clarifier pour tous ceux qui lisent cette réponse: "le sélecteur est de type @objc func ..." signifie que la fonction associée à #selectordoit être annotée @objc. Par exemple: @objc func keyboardShow() { ... }Cela m'a jeté une minute dans Swift 4!
leanne
0

Swift 5 et Xcode 10.2:

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

Observateur de notification 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
la source