Jak przekazać obiekt za pomocą NSNotificationCenter

130

Próbuję przekazać obiekt z delegata mojej aplikacji do odbiorcy powiadomień w innej klasie.

Chcę przekazać liczbę całkowitą messageTotal. W tej chwili mam:

W odbiorniku:

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

W klasie wykonującej powiadomienie:

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

Ale chcę przekazać obiekt messageTotaldrugiej klasie.

Jon
źródło
dla swift 2.0 i swift 3.0 stackoverflow.com/questions/36910965/…
Sahil

Odpowiedzi:

236

Będziesz musiał użyć wariantu „userInfo” i przekazać obiekt NSDictionary, który zawiera wartość całkowitą messageTotal:

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

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

Po stronie odbierającej dostęp do słownika userInfo można uzyskać w następujący sposób:

-(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);
    }
}
LearnCocos2D
źródło
Dzięki, ustawiam messageTotalodznakę na UIButton, czy wiesz, jak mogę odświeżyć przycisk za pomocą nowej liczby odznak? Kod do wyświetlenia obrazu viewDidLoadtoUIBarButtonItem *eRXButton = [BarButtonBadge barButtonWithImage:buttonImage badgeString:@"1" atRight:NO toTarget:self action:@selector(eRXButtonPressed)];
Jon
Nie jestem pewien, dlaczego musisz porównać notyfikację.nazwa. Mapowanie nazwy powinno być wykonane podczas wykonywania addObserver (). Funkcja takeTestNotification powinna być wywoływana tylko wtedy, gdy obserwuje się określone powiadomienie.
Johan Karlsson,
1
Johan, w tym prostym przypadku masz rację, ale możliwe jest, że wiele powiadomień uruchamia ten sam program obsługi
Lytic
94

Opierając się na dostarczonym rozwiązaniu, pomyślałem, że pomocne może być pokazanie przykładu przekazywania własnego niestandardowego obiektu danych (do którego odnosiłem się tutaj jako „wiadomość” zgodnie z pytaniem).

Klasa A (nadawca):

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

Klasa B (odbiornik):

- (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
źródło
2
dlaczego ta odpowiedź nie ma więcej głosów za ?! działa doskonale i nie jest hackem!
Reuben Tanner
4
@Kairos, ponieważ nie jest przeznaczony do użytku w ten sposób. objectparam się postNotificationName powinno czyli ten, który wysłania tego powiadomienia.
xi.lin
2
Tak, obiekt powinien zostać przekazany jako NSDictionary przy użyciu userInfoparametru, a zaakceptowana odpowiedź powyżej została zmieniona, aby to pokazać.
David Douglas
1
To bardzo mylące, dlaczego ta odpowiedź ma tak wiele głosów za? Należy to usunąć. Każdy powinien używać userInfo, które zostało stworzone właśnie do tego.
Shinnyx,
Ok, dzięki za informację zwrotną ... Zaktualizowałem odpowiedź, aby użyć userInfosłownika jako sposobu przekazywania danych obiektu.
David Douglas,
27

Wersja Swift 2

Jak zauważył @Johan Karlsson ... Zrobiłem to źle. Oto właściwy sposób wysyłania i otrzymywania informacji za pomocą NSNotificationCenter.

Najpierw przyjrzymy się inicjalizatorowi postNotificationName:

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

źródło

Będziemy przekazywać nasze informacje za pomocą userInfoparametru. [NSObject : AnyObject]Typ to hold-over z Objective-C . Tak więc w krainie Swift wszystko, co musimy zrobić, to przekazać słownik języka Swift, który ma klucze, z których pochodzą NSObjecti wartości, które mogą być AnyObject.

Mając tę ​​wiedzę tworzymy słownik, który przekażemy do objectparametru:

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

Następnie przekazujemy słownik do naszego parametru obiektu.

Nadawca

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

Klasa odbiornika

Najpierw musimy się upewnić, że nasza klasa obserwuje powiadomienie

override func viewDidLoad() {
    super.viewDidLoad()

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

Wtedy możemy otrzymać nasz słownik:

func btnClicked(notification: NSNotification) {
   let userInfo : [String:String!] = notification.userInfo as! [String:String!]
   let name = userInfo["UserName"]
   print(name)
}
Dan Beaulieu
źródło
W rzeczywistości naruszasz zamierzone użycie postNotificationName (). Ale nie jesteś sam. Widziałem wielu programistów używających parametru obiektu do wysyłania obiektów użytkowników. Drugi argument, obiekt, jest zarezerwowany dla nadawcy. Naprawdę powinieneś użyć userInfo do wysyłania wszelkiego rodzaju obiektów. W przeciwnym razie możesz napotkać przypadkowe awarie itp.
Johan Karlsson
26

Szybki 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 (który zdecydowanie powinieneś zrobić!):

Wymienić Notification.Name("SomeNotificationName")z .someNotificationName:

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

Wymień "key0"i "key1"z Notification.Key.key0a Notification.Key.key1:

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

Dlaczego zdecydowanie powinienem to zrobić? Aby uniknąć kosztownych błędów literowych, ciesz się zmianą nazwy, korzystaj z wyszukiwania itp.

frouo
źródło
Dzięki. Pozornie rozszerzenie Notification.Name jest możliwe, ale nie Notification.Key. 'Key' is not a member type of 'Notification'. Zobacz tutaj: https://ibb.co/hDQYbd2
alpennec
Dziękuję, wygląda na to, że Keystruktura została usunięta od tego czasu. Aktualizuję odpowiedź
dnia
2

Swift 5.1 Custom Object / Type

// 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
źródło