NSNotificationCenter addObserver w Swift

392

Jak dodać obserwatora w Swift do domyślnego centrum powiadomień? Próbuję przenieść ten wiersz kodu, który wysyła powiadomienie o zmianie poziomu baterii.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
Niebieska Jagoda
źródło
O co konkretnie pytasz? Jak działa selektor?
nschum
1
Nie zdawałem sobie sprawy, że typ „Selector” to po prostu napis w Swift. Brak wzmianki o tym w dokumentach.
Berry Blue

Odpowiedzi:

442

Jest taki sam jak interfejs API Objective-C, ale używa składni Swift.

Swift 4.2 i Swift 5:

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

Jeśli Twój obserwator nie dziedziczy po obiekcie Objective-C, musisz @objcużyć prefiksu metody , aby użyć go jako selektora.

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

Zobacz NSNotificationCenter Class Reference , Interakcja z interfejsami API Objective-C

Connor
źródło
3
Dzięki! Nie wiedziałem, jak przekazać nazwę selektora w Swift.
Berry Blue
14
@BerryBlue, czy powyższe rozwiązanie działało dla Ciebie? Uważam, że musisz zmienić „batteryLevelChanged” na „batteryLevelChanged:”, jeśli twoja funkcja akceptuje NSNotification jako parametr.
Olszańsk
1
@Olshansk Tak, masz rację. Potrzebujesz tego. Dzięki!
Berry Blue
dlaczego UIDeviceBatteryLevelDidChangeNotificationnie ma w cytatach? To typ struny.
kmiklas
13
Dodaj adnotację do klasy lub metody docelowej za pomocą @objc.
Klaas
757

Swift 4.0 i Xcode 9.0+:

Wyślij (Post) Powiadomienie:

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

LUB

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

Otrzymuj (otrzymuj) powiadomienie:

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

Moduł obsługi funkcji dla otrzymanego powiadomienia:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 i Xcode 8.0+:

Wyślij (Post) Powiadomienie:

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

Otrzymuj (otrzymuj) powiadomienie:

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

Procedura obsługi otrzymanego powiadomienia:

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

Usuń powiadomienie:

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

Swift 2.3 i Xcode 7:

Wyślij (post) powiadomienie

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

Otrzymuj (otrzymuj) powiadomienie

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

Procedura obsługi otrzymanego powiadomienia

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


Dla historycznych wersji Xcode ...



Wyślij (post) powiadomienie

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

Otrzymuj (otrzymuj) powiadomienie

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

Usuń powiadomienie

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Remove from all notifications being observed

Procedura obsługi otrzymanego powiadomienia

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

Adnotuj klasę lub metodę docelową za pomocą @objc

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

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}
Renish Dadhaniya
źródło
21
Dodaj adnotację do klasy lub metody docelowej za pomocą @objc.
Klaas
1
@goofansu Jesteś pewien? Uważam, że musisz to dodać, gdy jest to czysta klasa Swift.
Klaas
10
methodOFReceivedNoticationmusi być opatrzony adnotacjami dynamiclub należeć do podklasy NSObject.
Klaas
1
Jeśli nie, otrzymam ostrzeżenie o czasie wykonywania object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas
2
@TaylorAllred, Dziękuję bardzo za przejrzenie mojej odpowiedzi. Naprawdę doceniam twoją sugestię. Zmieniłem to. Sprawdź to.
Renish Dadhaniya
46

Dobrym sposobem na to jest użycie addObserver(forName:object:queue:using:)metody, a nie addObserver(_:selector:name:object:)metody często używanej z kodu Objective-C. Zaletą pierwszego wariantu jest to, że nie musisz używać @objcatrybutu w metodzie:

    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)

i możesz nawet użyć zamknięcia zamiast metody, jeśli chcesz:

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

Możesz użyć zwróconej wartości, aby zatrzymać nasłuchiwanie powiadomienia później:

    NotificationCenter.default.removeObserver(observer)

Inną zaletą tej metody było to, że nie wymagała użycia ciągów selektora, których kompilator nie mógł sprawdzić statycznie, a więc były podatne na zerwanie, jeśli zmieniono nazwę metody, ale Swift 2.2 i później zawierają #selectorwyrażenia, które rozwiązują ten problem.

Jon Colverson
źródło
7
To jest świetne! Dla kompletności chciałbym tylko zobaczyć przykład wyrejestrowania. Zupełnie inaczej niż addObserver(_:selector:name:object:) sposób wyrejestrowania. Musisz zatrzymać obiekt addObserverForName(_:object:queue:usingBlock:)i przekazać goremoveObserver:
Lucasowi Goossenowi
1
Wymaga to aktualizacji, aby uwzględnić wyrejestrowanie zwróconego sprzeciwu addObserverForName(_:object:queue:usingBlock:).
Hyperbole
3
Jest to o wiele lepsza odpowiedź niż Connora lub Renisha (oba powyżej w czasie tego komentarza), ponieważ omija konieczność korzystania z metod Obj-C #selector. Rezultat jest znacznie szybszy i bardziej poprawny, IMO. Dzięki!
patr1ck
2
Pamiętaj, że jeśli użyjesz tego w powiedzmy a UIViewControlleri odniesiesz się do selftego zamknięcia, musisz użyć [weak self]lub będziesz miał cykl odniesienia i wyciek pamięci.
Rob N
40

Swift 3.0 w Xcode 8

Swift 3.0 zastąpił wiele interfejsów API o „ciągłym typie” struct„typami opakowań”, jak ma to miejsce w przypadku NotificationCenter. Powiadomienia są teraz identyfikowane przez struct Notfication.Nameraczej niż przez String. Zobacz przewodnik Migracja do Swift 3 .

Poprzednie użycie:

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

Nowe zastosowanie 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)

Wszystkie typy powiadomień systemowych są teraz zdefiniowane jako stałe statyczne Notification.Name; czyli .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, itd.

Możesz rozszerzyć Notification.Nameswoje własne niestandardowe powiadomienia, aby zachować spójność z powiadomieniami systemowymi:

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Jeffrey Fulton
źródło
24
  1. Zadeklaruj nazwę powiadomienia

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Możesz dodać obserwatora na dwa sposoby:

    Za pomocą 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"]
    }

    lub za pomocą 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. Opublikuj swoje powiadomienie

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

z iOS 9 i OS X 10.11. Obserwator NSNotificationCenter nie musi już się wyrejestrowywać po zwolnieniu. więcej informacji

W przypadku blockimplementacji bazowej musisz wykonać słabo silny taniec, jeśli chcesz użyć selfw bloku. więcej informacji

Obserwatorzy z blokami muszą zostać usunięci więcej informacji

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)
Warif Akhand Rishi
źródło
5
„z iOS 9 i OS X 10.11. Obserwator NSNotificationCenter nie musi już się wyrejestrowywać po zwolnieniu.” Dotyczy to tylko obserwatorów opartych na Selektorze. Obserwatorzy z blokami nadal muszą zostać usunięci.
Abhinav,
8

Przekaż dane za pomocą NSNotificationCenter

Możesz również przekazywać dane za pomocą NotificationCentre w swift 3.0 i NSNotificationCenter w swift 2.0.

Wersja Swift 2.0

Przekazywać informacje za pomocą userInfo, który jest opcjonalnym słownikiem typu [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   
  }
}

Wersja Swift 3.0

UserInfo ma teraz [AnyHashable: Any]? jako argument, który podajemy jako dosłowny słownik w 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   
  }
}

Przekazywanie danych źródłowych za pomocą NotificationCentre (swift 3.0) i NSNotificationCenter (swift 2.0)

Sahil
źródło
Miło to słyszeć, pomogło ci :)
Sahil
6

W Swift 5

Powiedzmy, że jeśli chcesz odbierać dane z ViewControllerB do ViewControllerA

ViewControllerA (odbiornik)

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 (Sender)

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

Jestem w stanie wykonać jedną z następujących czynności, aby z powodzeniem korzystać z selektora - bez adnotacji w @objc:

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

LUB

let notificationSelector: Selector = "batteryLevelChanged:"

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

Moja wersja xcrun pokazuje Swift 1.2, a to działa na Xcode 6.4 i Xcode 7 beta 2 (które, jak myślałem, będą używać Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)
leanne
źródło
Nie musisz adnotować, @objcjeśli twoja klasa obserwatora dziedziczy NSObject.
Antonio Favata,
I nie powinieneś jawnie rzucać żadnego Stringz Selectornich. :)
Antonio Favata,
@alfvata: Moja klasa obserwatorów nie dziedziczy po NSObject. Dziedziczy po AnyObject w stylu Swift. Jawne przekazanie łańcucha do Selektora pozwala mi uniknąć wykonywania innych obejść związanych z celem C.
leanne
Nie jestem pewien, czy rozumiem, jak to działa. Usunąłem @objcadnotację z metody w mojej NSObjectklasie innej niż obserwator, dodałem as Selectorrzutowanie do Stringnazwy selektora, a gdy powiadomienie się uruchomi, aplikacja ulega awarii. Moja wersja Swift jest dokładnie taka sama jak Twoja.
Antonio Favata,
3
@alfavata, nie wiem co ci powiedzieć. Teraz korzystam z Xcode Beta 4 i nadal działa. Mój projekt jest całkowicie szybki; nie ma komponentów Objective-C. Może to robi różnicę. Może jest coś innego w ustawieniach projektu. Istnieje wiele możliwości! Powiem: tak długo, jak @objcadnotacja działa dla Ciebie, a tak nie działa, to kontynuuj adnotacje!
leanne
2

W swift 2.2 - XCode 7.3 używamy #selectordoNSNotificationCenter

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)
Deepak Thakur
źródło
2

Powinniśmy również usunąć powiadomienie.

Dawny.

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

}
Pankaj Jangid
źródło
2
Uważam, że nie potrzebujesz tego od iOS 9. Odbywa się to automatycznie.
Viktor Kucera,
1

W swift 3, Xcode 8.2: - sprawdzanie poziomu naładowania baterii

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

NSNotificationCenter dodaje składnię obserwatora w Swift 4.0 dla iOS 11

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

Dotyczy to typu nazwy powiadomienia keyboardWillShow. Z dostępnej opcji można wybrać inny typ

selektor jest typu @objc func, który obsługuje sposób wyświetlania klawiatury (jest to funkcja użytkownika)

Ashim Dahal
źródło
Aby wyjaśnić każdemu, kto czyta tę odpowiedź: „Selektor jest typu @objc func ...” oznacza, że ​​przypisana do niego funkcja #selectormusi być opatrzona adnotacjami @objc. Na przykład: @objc func keyboardShow() { ... }To rzuciło mnie na minutę w Swift 4!
leanne
0

Swift 5 i Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)
David.Chu.ca
źródło
0

Swift 5 Notification Observer

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