Come passare l'oggetto con NSNotificationCenter


129

Sto cercando di passare un oggetto dal delegato della mia app a un destinatario di notifica in un'altra classe.

Voglio passare numeri interi messageTotal. In questo momento ho:

Nel ricevitore:

- (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];

Nella classe che sta facendo la notifica:

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

Ma voglio passare l'oggetto messageTotalall'altra classe.


Risposte:


235

Dovrai utilizzare la variante "userInfo" e passare un oggetto NSDictionary che contiene il numero intero MessageTotal:

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

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

All'estremità ricevente è possibile accedere al dizionario userInfo come segue:

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

Grazie, sto impostando messageTotalun badge su un UIButton, sai come posso aggiornare il pulsante con il nuovo conteggio dei badge? Il codice per visualizzare l'immagine viewDidLoadèUIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];
Jon,

Non sono sicuro del perché sia ​​necessario confrontare il nome notification.name. La mappatura del nome deve essere eseguita quando si esegue addObserver (). ReceTestNotification deve essere chiamato solo quando si osserva una notifica specifica.
Johan Karlsson,

1
Johan, in questo semplice caso hai ragione, ma è possibile che più notifiche
attivino

93

Basandomi sulla soluzione fornita, ho pensato che potesse essere utile mostrare un esempio passando il tuo oggetto dati personalizzato (che ho indicato qui come "messaggio" come da domanda).

Classe A (mittente):

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 (ricevitore):

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

2
perché questa risposta non ha più voti ?! funziona perfettamente e non è un trucco!
Reuben Tanner,

4
@Kairos perché non è progettato per l'uso in questo modo. il objectparametro in postNotificationName dovrebbe indicare quello che invia questa notifica.
xi.lin,

2
Sì, l'oggetto deve essere passato come NSDictionary usando il userInfoparametro e la risposta accettata sopra è stata ora modificata per mostrarlo.
David Douglas,

1
Questo è molto fuorviante, perché quella risposta ha così tanti voti positivi? Questo dovrebbe essere cancellato. Tutti dovrebbero usare userInfo che è stato creato esattamente per questo.
Shinnyx,

Ok, grazie per il feedback ... Ho aggiornato la risposta per usare il userInfodizionario come modo per passare i dati dell'oggetto.
David Douglas,

27

Versione Swift 2

Come ha sottolineato @Johan Karlsson ... stavo sbagliando. Ecco il modo corretto di inviare e ricevere informazioni con NSNotificationCenter.

Innanzitutto, esaminiamo l'inizializzatore per postNotificationName:

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

fonte

Trasmetteremo le nostre informazioni usando il userInfoparametro Il [NSObject : AnyObject]tipo è un hold-over da Objective-C . Quindi, nella terra di Swift, tutto ciò che dobbiamo fare è passare in un dizionario Swift che ha chiavi da cui derivano NSObjecte valori che possono essere AnyObject.

Con questa conoscenza creiamo un dizionario che passeremo al objectparametro:

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

Quindi passiamo il dizionario nel nostro parametro oggetto.

Mittente

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

Classe del ricevitore

Per prima cosa dobbiamo assicurarci che la nostra classe stia osservando la notifica

override func viewDidLoad() {
    super.viewDidLoad()

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

Quindi possiamo ricevere il nostro dizionario:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}

In realtà stai violando l'uso previsto di postNotificationName (). Ma tu non sei solo. Ho visto molti sviluppatori utilizzare il parametro object per inviare oggetti utente. Il secondo argomento, l'oggetto, è riservato al mittente. Dovresti davvero utilizzare l'utenteInfo per inviare tutti i tipi di oggetti. Altrimenti potresti riscontrare crash casuali ecc.
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 (che dovresti assolutamente fare!):

Sostituisci Notification.Name("SomeNotificationName")con .someNotificationName:

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

Sostituisci "key0"e "key1"con Notification.Key.key0e Notification.Key.key1:

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

Perché dovrei assolutamente farlo? Per evitare costosi errori di battitura, goditi la ridenominazione, divertiti a trovare l'utilizzo ecc ...


Grazie. Apparentemente è possibile estendere Notification.Name ma non Notification.Key. 'Key' is not a member type of 'Notification'. Vedi qui: https://ibb.co/hDQYbd2
alpennec

Grazie, sembra che Keyda allora sia stata rimossa la struttura. Sto aggiornando la risposta
frouo il

1

Swift 5.1 Oggetto / tipo personalizzato

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




}

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.