Comment passer un objet avec NSNotificationCenter

129

J'essaie de transmettre un objet de mon délégué d'application à un récepteur de notification dans une autre classe.

Je veux passer un entier messageTotal. En ce moment j'ai:

Dans le récepteur:

- (void) receiveTestNotification:(NSNotification *) notification
{
    if ([[notification name] isEqualToString:@"TestNotification"])
        NSLog (@"Successfully received the test notification!");
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissSheet) name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveTestNotification:) name:@"eRXReceived" object:nil];

Dans la classe qui effectue la notification:

[UIApplication sharedApplication].applicationIconBadgeNumber = messageTotal;
[[NSNotificationCenter defaultCenter] postNotificationName:@"eRXReceived" object:self];

Mais je veux passer l'objet messageTotalà l'autre classe.

Jon
la source
pour swift 2.0 et swift 3.0 stackoverflow.com/questions/36910965/…
Sahil

Réponses:

235

Vous devrez utiliser la variante "userInfo" et passer un objet NSDictionary qui contient l'entier messageTotal:

NSDictionary* userInfo = @{@"total": @(messageTotal)};

NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:@"eRXReceived" object:self userInfo:userInfo];

Du côté récepteur, vous pouvez accéder au dictionnaire userInfo comme suit:

-(void) receiveTestNotification:(NSNotification*)notification
{
    if ([notification.name isEqualToString:@"TestNotification"])
    {
        NSDictionary* userInfo = notification.userInfo;
        NSNumber* total = (NSNumber*)userInfo[@"total"];
        NSLog (@"Successfully received test notification! %i", total.intValue);
    }
}
ApprendreCocos2D
la source
Merci, je mets messageTotalun badge sur un UIButton, savez-vous comment je peux actualiser le bouton avec le nouveau nombre de badges? Le code pour afficher l'image viewDidLoadestUIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];
Jon
Je ne sais pas pourquoi vous devez comparer la notification.name. Le mappage du nom doit être effectué lorsque vous exécutez addObserver (). Le receiveTestNotification ne doit être appelé que lors de l'observation d'une notification spécifique.
Johan Karlsson
1
Johan, dans ce cas simple vous avez raison, mais il est possible que plusieurs notifications déclenchent le même gestionnaire
Lytic
93

En m'appuyant sur la solution fournie, j'ai pensé qu'il pourrait être utile de montrer un exemple en passant votre propre objet de données personnalisé (que j'ai référencé ici comme un `` message '' selon la question).

Classe A (expéditeur):

YourDataObject *message = [[YourDataObject alloc] init];
// set your message properties
NSDictionary *dict = [NSDictionary dictionaryWithObject:message forKey:@"message"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationMessageEvent" object:nil userInfo:dict];

Classe B (récepteur):

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter]
     addObserver:self selector:@selector(triggerAction:) name:@"NotificationMessageEvent" object:nil];
}

#pragma mark - Notification
-(void) triggerAction:(NSNotification *) notification
{
    NSDictionary *dict = notification.userInfo;
    YourDataObject *message = [dict valueForKey:@"message"];
    if (message != nil) {
        // do stuff here with your message data
    }
}
David Douglas
la source
2
pourquoi cette réponse n'a-t-elle pas plus de votes positifs?! cela fonctionne parfaitement et n'est pas un hack!
Reuben Tanner
4
@Kairos car il n'est pas conçu pour être utilisé comme ça. le objectparamètre in postNotificationName devrait signifier celui qui envoie cette notification.
xi.lin
2
Oui, l'objet doit être passé en tant que NSDictionary à l'aide du paramètre userInfoet la réponse acceptée ci-dessus a maintenant été modifiée pour le montrer.
David Douglas le
1
C'est très trompeur, pourquoi cette réponse a-t-elle autant de votes positifs? Cela devrait être supprimé. Tout le monde devrait utiliser userInfo qui a été créé exactement pour cela.
Shinnyx
Ok, merci pour les commentaires ... J'ai mis à jour la réponse pour utiliser le userInfodictionnaire comme moyen de transmettre les données de l'objet.
David Douglas
27

Version Swift 2

Comme @Johan Karlsson l'a souligné ... je faisais mal. Voici la manière appropriée d'envoyer et de recevoir des informations avec NSNotificationCenter.

Tout d'abord, nous examinons l'initialiseur pour postNotificationName:

init(name name: String,
   object object: AnyObject?,
 userInfo userInfo: [NSObject : AnyObject]?)

la source

Nous passerons nos informations en utilisant le userInfoparamètre. Le [NSObject : AnyObject]type est un vestige d' Objective-C . Donc, dans Swift land, tout ce que nous avons à faire est de passer dans un dictionnaire Swift qui a des clés dérivées NSObjectet des valeurs qui peuvent l'être AnyObject.

Avec cette connaissance, nous créons un dictionnaire que nous passerons dans le objectparamètre:

 var userInfo = [String:String]()
 userInfo["UserName"] = "Dan"
 userInfo["Something"] = "Could be any object including a custom Type."

Ensuite, nous passons le dictionnaire dans notre paramètre objet.

Expéditeur

NSNotificationCenter.defaultCenter()
    .postNotificationName("myCustomId", object: nil, userInfo: userInfo)

Classe de récepteur

Nous devons d'abord nous assurer que notre classe observe la notification

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("btnClicked:"), name: "myCustomId", object: nil)   
}
    

Ensuite, nous pouvons recevoir notre dictionnaire:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}
Dan Beaulieu
la source
Vous enfreignez en fait l'utilisation prévue de postNotificationName (). Mais vous n'êtes pas seul. J'ai vu de nombreux développeurs utiliser le paramètre object pour envoyer des objets utilisateur. Le deuxième argument, l'objet, est réservé à l'expéditeur. Vous devez vraiment utiliser userInfo pour envoyer tout type d'objets. Sinon, vous pourriez rencontrer des plantages aléatoires, etc.
Johan Karlsson
25

Swift 5

func post() {
    NotificationCenter.default.post(name: Notification.Name("SomeNotificationName"), 
        object: nil, 
        userInfo:["key0": "value", "key1": 1234])
}

func addObservers() {
    NotificationCenter.default.addObserver(self, 
        selector: #selector(someMethod), 
        name: Notification.Name("SomeNotificationName"), 
        object: nil)
}

@objc func someMethod(_ notification: Notification) {
    let info0 = notification.userInfo?["key0"]
    let info1 = notification.userInfo?["key1"]
}

Bonus (que vous devez absolument faire!):

Remplacez Notification.Name("SomeNotificationName")par .someNotificationName:

extension Notification.Name {
    static let someNotificationName = Notification.Name("SomeNotificationName")
}

Remplacez "key0"et "key1"par Notification.Key.key0et Notification.Key.key1:

extension Notification {
  enum Key: String {
    case key0
    case key1
  }
}

Pourquoi devrais-je vraiment faire ça? Pour éviter les erreurs de frappe coûteuses, profitez du changement de nom, profitez de la recherche d'utilisation, etc.

frouo
la source
Merci. Apparemment, l'extension de Notification.Name est possible mais pas de Notification.Key. 'Key' is not a member type of 'Notification'. Voir ici: https://ibb.co/hDQYbd2
alpennec
Merci, il semble que Keystruct a été supprimé depuis. Je mets à jour la réponse
frouo
1

Objet / type personnalisé Swift 5.1

// MARK: - NotificationName
// Extending notification name to avoid string errors.
extension Notification.Name {
    static let yourNotificationName = Notification.Name("yourNotificationName")
}


// MARK: - CustomObject
class YourCustomObject {
    // Any stuffs you would like to set in your custom object as always.
    init() {}
}

// MARK: - Notification Sender Class
class NotificatioSenderClass {

     // Just grab the content of this function and put it to your function responsible for triggering a notification.
    func postNotification(){
        // Note: - This is the important part pass your object instance as object parameter.
        let yourObjectInstance = YourCustomObject()
        NotificationCenter.default.post(name: .yourNotificationName, object: yourObjectInstance)
    }
}

// MARK: -Notification  Receiver class
class NotificationReceiverClass: UIViewController {
    // MARK: - ViewController Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Register your notification listener
        NotificationCenter.default.addObserver(self, selector: #selector(didReceiveNotificationWithCustomObject), name: .yourNotificationName, object: nil)
    }

    // MARK: - Helpers
    @objc private func didReceiveNotificationWithCustomObject(notification: Notification){
        // Important: - Grab your custom object here by casting the notification object.
        guard let yourPassedObject = notification.object as? YourCustomObject else {return}
        // That's it now you can use your custom object
        //
        //

    }
      // MARK: - Deinit
  deinit {
      // Save your memory by releasing notification listener
      NotificationCenter.default.removeObserver(self, name: .yourNotificationName, object: nil)
    }




}

Mussa Charles
la source