NSNotificationCenter addObserver in Swift

392

Wie füge ich einen Beobachter in Swift zum Standard-Benachrichtigungscenter hinzu? Ich versuche, diese Codezeile zu portieren, die eine Benachrichtigung sendet, wenn sich der Akkuladestand ändert.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
Beerenblau
quelle
Was fragst du konkret? Wie funktioniert der Selektor?
Nschum
1
Ich wusste nicht, dass der Typ "Selector" nur eine Zeichenfolge in Swift ist. Keine Erwähnung in den Dokumenten.
Berry Blue

Antworten:

442

Es ist dasselbe wie die Objective-C-API, verwendet jedoch die Swift-Syntax.

Swift 4.2 & Swift 5:

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

Wenn Ihr Beobachter nicht von einem Objective-C-Objekt erbt, müssen Sie Ihrer Methode ein Präfix voranstellen @objc, um sie als Selektor verwenden zu können.

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

Siehe NSNotificationCenter-Klassenreferenz , Interaktion mit Objective-C-APIs

Connor
quelle
3
Vielen Dank! Ich wusste nicht, wie ich den Selektornamen in Swift übergeben sollte.
Berry Blue
14
@BerryBlue, hat die obige Lösung für Sie funktioniert? Ich glaube, dass Sie "batterielevelChanged" in "batterielevelChanged:" ändern müssen, wenn Ihre Funktion die NSNotification als Parameter akzeptiert.
Olshansk
1
@ Olshansk Ja, du bist richtig. Das brauchst du. Vielen Dank!
Berry Blue
warum steht UIDeviceBatteryLevelDidChangeNotificationnicht in Anführungszeichen? Es ist ein String-Typ.
kmiklas
13
Stellen Sie sicher, dass Sie entweder die Klasse oder die Zielmethode mit Anmerkungen versehen @objc.
Klaas
757

Swift 4.0 & Xcode 9.0+:

Benachrichtigung senden (posten):

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

ODER

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

Benachrichtigung erhalten (erhalten):

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

Funktionsmethoden-Handler für empfangene Benachrichtigung:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

Benachrichtigung senden (posten):

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

Benachrichtigung erhalten (erhalten):

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

Methodenhandler für empfangene Benachrichtigung:

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

Benachrichtigung entfernen:

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

Swift 2.3 & Xcode 7:

Benachrichtigung senden (posten)

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

Benachrichtigung erhalten (erhalten)

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

Methodenhandler für empfangene Benachrichtigung

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


Für historische Xcode-Versionen ...



Benachrichtigung senden (posten)

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

Benachrichtigung erhalten (erhalten)

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

Benachrichtigung entfernen

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

Methodenhandler für empfangene Benachrichtigung

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

Kommentieren Sie entweder die Klasse oder die Zielmethode mit @objc

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

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}
Renish Dadhaniya
quelle
21
Stellen Sie sicher, dass Sie entweder die Klasse oder die Zielmethode mit Anmerkungen versehen @objc.
Klaas
1
@goofansu Bist du sicher? Ich denke, dass Sie es hinzufügen müssen, wenn es eine reine Swift-Klasse ist.
Klaas
10
methodOFReceivedNoticationmuss entweder mit dynamiceiner Unterklasse von NSObject versehen sein oder Mitglied einer Unterklasse von NSObject sein.
Klaas
1
Wenn nicht, bekomme ich eine Laufzeitwarnung object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas
2
@ TaylorAllred, vielen Dank für die Überprüfung meiner Antwort. Ich freue mich sehr über Ihren Vorschlag. Ich habe es geändert. Bitte überprüfen sie es.
Renish Dadhaniya
46

Eine gute Möglichkeit, dies zu tun, besteht darin, die addObserver(forName:object:queue:using:)Methode anstelle der addObserver(_:selector:name:object:)Methode zu verwenden, die häufig aus Objective-C-Code verwendet wird. Der Vorteil der ersten Variante besteht darin, dass Sie das @objcAttribut für Ihre Methode nicht verwenden müssen :

    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)

und Sie können sogar einfach einen Verschluss anstelle einer Methode verwenden, wenn Sie möchten:

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

Sie können den zurückgegebenen Wert verwenden, um die Benachrichtigung später nicht mehr abzuhören:

    NotificationCenter.default.removeObserver(observer)

Früher hatte die Verwendung dieser Methode einen weiteren Vorteil: Sie mussten keine Selektorzeichenfolgen verwenden, die vom Compiler nicht statisch überprüft werden konnten, und waren daher anfällig für Unterbrechungen, wenn die Methode umbenannt wurde, aber Swift 2.2 und Fügen Sie später #selectorAusdrücke hinzu , die dieses Problem beheben.

Jon Colverson
quelle
7
Das ist toll! Der Vollständigkeit halber möchte ich auch nur ein Beispiel ohne Registrierung sehen. Es ist ganz anders als die addObserver(_:selector:name:object:) Art der Abmeldung. Sie müssen das zurückgegebene Objekt behalten addObserverForName(_:object:queue:usingBlock:)und anremoveObserver:
Lucas Goossen
1
Dies muss aktualisiert werden, um die Abmeldung des von zurückgegebenen Objekts zu ermöglichen addObserverForName(_:object:queue:usingBlock:).
Hyperbole
3
Dies ist eine viel bessere Antwort als die von Connor oder Renish (beide oben zum Zeitpunkt dieses Kommentars), da es darum geht, die Obj-C #selector-Methoden verwenden zu müssen. Das Ergebnis ist viel schneller und korrekter, IMO. Vielen Dank!
Patr1ck
2
Denken Sie daran, wenn Sie dies beispielsweise in a verwenden UIViewControllerund selfin diesem Abschluss darauf verweisen , müssen Sie es verwenden, [weak self]oder Sie haben einen Referenzzyklus und den Speicherverlust.
Rob N
40

Swift 3.0 in Xcode 8

Swift 3.0 hat viele "streng typisierte" APIs durch struct"Wrapper-Typen" ersetzt, wie dies bei NotificationCenter der Fall ist. Benachrichtigungen werden jetzt struct Notfication.Nameeher durch a als durch gekennzeichnet String. Weitere Informationen finden Sie im Handbuch Migrieren zu Swift 3 .

Vorherige Verwendung:

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

Neue Verwendung von 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)

Alle Systembenachrichtigungstypen sind jetzt als statische Konstanten für definiert Notification.Name. dh .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChangeetc.

Sie können Notification.Namemit Ihren eigenen benutzerdefinierten Benachrichtigungen erweitern, um mit den Systembenachrichtigungen konsistent zu bleiben:

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Jeffrey Fulton
quelle
24
  1. Deklarieren Sie einen Benachrichtigungsnamen

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Sie können Beobachter auf zwei Arten hinzufügen:

    Verwenden von 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"]
    }

    oder mit 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. Veröffentlichen Sie Ihre Benachrichtigung

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

von iOS 9 und OS X 10.11. Es ist nicht länger erforderlich, dass sich ein Beobachter des NSNotificationCenter beim Aufheben der Zuordnung von der Registrierung abmeldet. Mehr Info

Für eine blockbasierte Implementierung müssen Sie einen schwach-starken Tanz ausführen, wenn Sie selfinnerhalb des Blocks verwenden möchten . Mehr Info

Blockbasierte Beobachter müssen entfernt werden, um weitere Informationen zu erhalten

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)
Warif Akhand Rishi
quelle
5
"Ab iOS 9 und OS X 10.11. Es ist nicht mehr erforderlich, dass sich ein Beobachter des NSNotificationCenter beim Aufheben der Zuordnung von der Registrierung abmeldet." Dies gilt nur für Beobachter auf Selektorbasis. Blockbasierte Beobachter müssen noch entfernt werden.
Abhinav
8

Übergeben Sie Daten mit NSNotificationCenter

Sie können Daten auch mit NotificationCentre in Swift 3.0 und NSNotificationCenter in Swift 2.0 übergeben.

Swift 2.0 Version

Informationen mit userInfo übergeben, einem optionalen Wörterbuch vom Typ [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   
  }
}

Swift 3.0 Version

Die userInfo nimmt jetzt [AnyHashable: Any]? als Argument, das wir in Swift als Wörterbuchliteral bereitstellen

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

Quelle Passdaten mit NotificationCentre (swift 3.0) und NSNotificationCenter (swift 2.0)

Sahil
quelle
Ich bin froh zu hören, dass es dir geholfen hat :)
Sahil
6

In Swift 5

Angenommen, Sie möchten Daten von ViewControllerB an ViewControllerA empfangen

ViewControllerA (Empfänger)

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

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
quelle
2

Ich kann einen der folgenden Schritte ausführen, um einen Selektor erfolgreich zu verwenden - ohne mit @objc etwas zu kommentieren:

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

ODER

let notificationSelector: Selector = "batteryLevelChanged:"

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

Meine xcrun-Version zeigt Swift 1.2 und dies funktioniert unter Xcode 6.4 und Xcode 7 Beta 2 (von denen ich dachte, dass sie Swift 2.0 verwenden würden):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)
Leanne
quelle
Sie müssen nicht mit Anmerkungen versehen, @objcwenn Ihre Beobachterklasse von erbt NSObject.
Antonio Favata
Und Sie sollten auch nicht explizit ein Stringzu werfen müssen Selector. :)
Antonio Favata
@alfvata: Meine Beobachterklasse erbt nicht von NSObject. Es erbt von AnyObject im Swift-Stil. Durch das explizite Umwandeln der Zeichenfolge in Selector kann ich vermeiden, dass andere objektiv-C-bezogene Problemumgehungen durchgeführt werden.
Leanne
Ich bin mir nicht sicher, ob ich verstehe, wie das funktioniert. Ich habe die @objcAnmerkung aus der Methode in meiner Nicht- NSObjectBeobachter-Klasse entfernt, das as SelectorCasting zum StringSelektornamen hinzugefügt , und wenn die Benachrichtigung ausgelöst wird, stürzt die App ab. Meine Swift-Version ist genau die gleiche wie deine.
Antonio Favata
3
@alfavata, ich weiß nicht, was ich dir sagen soll. Ich bin jetzt auf Xcode Beta 4 und es funktioniert immer noch. Mein Projekt ist total schnell; Es gibt keine Objective-C-Komponenten. Vielleicht macht das einen Unterschied. Vielleicht gibt es etwas anderes in den Projekteinstellungen. Es gibt beliebig viele Möglichkeiten! Ich sage: Solange die @objcAnnotation für Sie funktioniert und auf diese Weise nicht, kommentieren Sie weiter!
Leanne
2

In Swift 2.2 - XCode 7.3 verwenden wir #selectorfürNSNotificationCenter

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

Wir sollten auch die Benachrichtigung entfernen.

Ex.

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

}
Pankaj Jangid
quelle
2
Ich glaube, Sie brauchen das seit iOS 9 nicht mehr. Es wird automatisch gemacht.
Viktor Kucera
1

In Swift 3, Xcode 8.2: - Überprüfen des Batteriestatus

//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
quelle
1

NSNotificationCenter fügt Beobachter-Syntax in Swift 4.0 für iOS 11 hinzu

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

Dies gilt für den Benachrichtigungstyp "keyboardWillShow". Ein anderer Typ kann aus der verfügbaren Option ausgewählt werden

Der Selektor ist vom Typ @objc func und regelt, wie die Tastatur angezeigt wird (dies ist Ihre Benutzerfunktion).

Ashim Dahal
quelle
Nur um jedem, der diese Antwort liest, klar zu stellen: "Der Selektor ist vom Typ @objc func ..." bedeutet, dass die zugeordnete Funktion mit #selectorAnmerkungen versehen werden muss @objc. Zum Beispiel: @objc func keyboardShow() { ... }Das hat mich in Swift 4 für eine Minute geworfen!
Leanne
0

Swift 5 & Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)
David.Chu.ca
quelle
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
quelle