NSNotificationCenter addObserver di Swift

393

Bagaimana Anda menambahkan pengamat di Swift ke pusat pemberitahuan default? Saya mencoba untuk mem-porting baris kode ini yang mengirimkan pemberitahuan ketika level baterai berubah.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
Berry Blue
sumber
Apa yang Anda minta secara spesifik? Bagaimana cara pemilih bekerja?
nschum
1
Saya tidak menyadari bahwa tipe "Selector" hanyalah string di Swift. Tidak disebutkan dalam dokumen.
Berry Blue

Jawaban:

443

Itu sama dengan Objective-C API, tetapi menggunakan sintaks Swift.

Swift 4.2 & Swift 5:

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

Jika pengamat Anda tidak mewarisi dari objek Objective-C, Anda harus mengawali metode Anda @objcuntuk menggunakannya sebagai pemilih.

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

Lihat Referensi Kelas NSNotificationCenter , Berinteraksi dengan API Objective-C

Connor
sumber
3
Terima kasih! Saya tidak tahu cara memasukkan nama pemilih di Swift.
Berry Blue
14
@ BlackBerryBlue, apakah solusi di atas berfungsi untuk Anda? Saya percaya bahwa Anda perlu mengubah "batteryLevelChanged" menjadi "batteryLevelChanged:" jika fungsi Anda menerima NSNotification sebagai parameter.
Olshansk
1
@Olshansk Ya, Anda benar. Anda memang butuh itu. Terima kasih!
Berry Blue
mengapa UIDeviceBatteryLevelDidChangeNotificationtidak ada dalam tanda kutip? Ini adalah tipe string.
kmiklas
13
Pastikan untuk membubuhi keterangan kelas atau metode target @objc.
Klaas
757

Swift 4.0 & Xcode 9.0+:

Kirim (Posting) Pemberitahuan:

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

ATAU

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

Terima (Dapatkan) Pemberitahuan:

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

Penangan Fungsi-Metode untuk Pemberitahuan yang diterima:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

Kirim (Posting) Pemberitahuan:

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

Terima (Dapatkan) Pemberitahuan:

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

Penangan metode untuk Notifikasi yang diterima:

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

Hapus Pemberitahuan:

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

Swift 2.3 & Xcode 7:

Kirim (Posting) Pemberitahuan

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

Terima (Dapatkan) Pemberitahuan

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

Penangan metode untuk Notifikasi yang diterima

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


Untuk versi Xcode yang bersejarah ...



Kirim (Posting) Pemberitahuan

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

Terima (Dapatkan) Pemberitahuan

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

Hapus Notifikasi

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

Penangan metode untuk Notifikasi yang diterima

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

Membubuhi keterangan kelas atau metode target dengan @objc

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

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}
Renish Dadhaniya
sumber
21
Pastikan untuk membubuhi keterangan kelas atau metode target @objc.
Klaas
1
@goofansu Anda yakin? Saya rasa Anda harus menambahkannya ketika itu adalah kelas Swift murni.
Klaas
10
methodOFReceivedNoticationharus dianotasi dengan dynamicatau menjadi anggota subkelas NSObject.
Klaas
1
Jika tidak, saya mendapatkan peringatan runtime object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas
2
@TaylorAllred, Terima kasih banyak telah meninjau jawaban saya. Saya sangat menghargai saran Anda. Saya telah mengubahnya. Harap tinjau.
Renish Dadhaniya
46

Cara yang baik untuk melakukan ini adalah dengan menggunakan addObserver(forName:object:queue:using:)metode daripada addObserver(_:selector:name:object:)metode yang sering digunakan dari kode Objective-C. Keuntungan dari varian pertama adalah Anda tidak harus menggunakan @objcatribut pada metode Anda:

    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)

dan Anda bahkan bisa menggunakan penutupan alih-alih metode jika Anda mau:

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

Anda dapat menggunakan nilai yang dikembalikan untuk berhenti mendengarkan notifikasi nanti:

    NotificationCenter.default.removeObserver(observer)

Dulu ada keuntungan lain dalam menggunakan metode ini, yaitu bahwa hal itu tidak mengharuskan Anda untuk menggunakan string pemilih yang tidak dapat diperiksa secara statis oleh kompiler dan sangat rapuh untuk dipecahkan jika metode ini diganti namanya, tetapi Swift 2.2 dan nanti termasuk #selectorekspresi yang memperbaiki masalah itu.

Jon Colverson
sumber
7
Ini bagus! Untuk kelengkapan saya hanya ingin melihat contoh tidak mendaftar juga. Sangat berbeda dengan addObserver(_:selector:name:object:) cara tidak mendaftar. Anda harus menyimpan objek kembali addObserverForName(_:object:queue:usingBlock:)dan meneruskannya keremoveObserver:
Lucas Goossen
1
Ini perlu diperbarui untuk memasukkan de-registrasi atas keberatan yang dikembalikan oleh addObserverForName(_:object:queue:usingBlock:).
Hyperbole
3
Ini adalah jawaban yang jauh lebih baik daripada jawaban Connor atau Renish (keduanya di atas pada saat komentar ini) karena harus menggunakan metode #jektor Obj-C #. Hasilnya jauh lebih cepat dan lebih benar, IMO. Terima kasih!
patr1ck
2
Ingat, jika Anda menggunakan ini dalam, katakanlah, UIViewControllerdan merujuk selfpada penutupan itu, Anda perlu menggunakan [weak self]atau Anda akan memiliki siklus referensi dan kebocoran memori.
Rob N
40

Swift 3.0 di Xcode 8

Swift 3.0 telah menggantikan banyak API "typly-typed" dengan struct"wrapper types", seperti halnya NotificationCenter. Pemberitahuan sekarang diidentifikasi oleh struct Notfication.Namebukan oleh String. Lihat panduan Migrasi ke Swift 3 .

Penggunaan sebelumnya :

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

Penggunaan Swift 3.0 baru:

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

Semua jenis notifikasi sistem sekarang didefinisikan sebagai konstanta statis pada Notification.Name; yaitu .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, dll

Anda dapat memperluas Notification.Namedengan notifikasi khusus Anda sendiri agar tetap konsisten dengan notifikasi sistem:

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Jeffrey Fulton
sumber
24
  1. Nyatakan nama notifikasi

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Anda dapat menambahkan pengamat dengan dua cara:

    Menggunakan 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"]
    }

    atau menggunakan 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. Posting pemberitahuan Anda

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

dari iOS 9 dan OS X 10.11. Pengamat NSNotificationCenter tidak diperlukan lagi untuk tidak mendaftar sendiri saat sedang tidak dialokasikan. Info lebih lanjut

Untuk blockimplementasi berbasis Anda perlu melakukan tarian lemah-kuat jika Anda ingin menggunakan selfdi dalam blok. Info lebih lanjut

Pengamat berbasis blok perlu dihapus info lebih lanjut

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)
Warif Akhand Rishi
sumber
5
"dari iOS 9 dan OS X 10.11. Pengamat NSNotificationCenter tidak perlu lagi mendaftar sendiri ketika sedang tidak dialokasikan." Ini berlaku hanya untuk pengamat berbasis Pemilih. Pengamat berbasis blok masih harus dihapus.
Abhinav
8

Lulus Data menggunakan NSNotificationCenter

Anda juga dapat meneruskan data menggunakan NotificationCentre di swift 3.0 dan NSNotificationCenter di swift 2.0.

Versi Swift 2.0

Lulus info menggunakan userInfo yang merupakan Kamus jenis opsional [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   
  }
}

Versi Swift 3.0

UserInfo sekarang menggunakan [AnyHashable: Any]? sebagai argumen, yang kami sediakan sebagai kamus literal di 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   
  }
}

Source pass data menggunakan NotificationCentre (swift 3.0) dan NSNotificationCenter (swift 2.0)

Sahil
sumber
Senang mendengarnya membantu Anda :)
Sahil
6

Dalam Swift 5

Katakanlah jika ingin Menerima Data dari ViewControllerB ke ViewControllerA

ViewControllerA (Penerima)

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

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

Saya dapat melakukan salah satu dari yang berikut ini untuk berhasil menggunakan pemilih - tanpa membubuhi keterangan apa pun dengan @objc:

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

ATAU

let notificationSelector: Selector = "batteryLevelChanged:"

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

Versi xcrun saya menunjukkan Swift 1.2, dan ini berfungsi pada Xcode 6.4 dan Xcode 7 beta 2 (yang saya pikir akan menggunakan Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)
leanne
sumber
Anda tidak perlu membuat catatan dengan @objcjika kelas pengamat Anda mewarisi dari NSObject.
Antonio Favata
Dan Anda seharusnya tidak perlu secara eksplisit melemparkan Stringke Selectorsalah satu. :)
Antonio Favata
@alfvata: Kelas pengamat saya tidak mewarisi dari NSObject. Ini mewarisi dari AnyObject, Swift-style. Secara eksplisit melemparkan string ke Selector memungkinkan saya untuk menghindari melakukan salah satu dari solusi yang berhubungan dengan Objective-C lainnya.
leanne
Saya tidak yakin saya mengerti cara kerjanya. Saya menghapus @objcanotasi dari metode di NSObjectkelas non- pengamat saya , menambahkan as Selectorcasting ke Stringnama pemilih, dan ketika pemberitahuan menyala aplikasi crash. Versi Swift saya persis sama dengan versi Swift Anda.
Antonio Favata
3
@alfavata, saya tidak tahu harus bilang apa. Saya sekarang menggunakan Xcode Beta 4, dan masih berfungsi. Proyek saya sepenuhnya Swift; tidak ada komponen Objective-C. Mungkin itu membuat perbedaan. Mungkin ada sesuatu yang berbeda dalam pengaturan proyek. Ada sejumlah kemungkinan! Saya akan mengatakan: selama @objcanotasi berfungsi untuk Anda, dan dengan cara ini tidak, maka terus anotasi!
leanne
2

Di swift 2.2 - XCode 7.3, kami gunakan #selectoruntukNSNotificationCenter

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

Kami juga harus menghapus notifikasi.

Ex.

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

}
Pankaj Jangid
sumber
2
Saya yakin Anda tidak memerlukan ini sejak iOS 9. Ini dilakukan secara otomatis.
Viktor Kucera
1

Dalam cepat 3, Xcode 8.2: - memeriksa tingkat status baterai

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

NSNotificationCenter menambahkan sintaksis pengamat di Swift 4.0 untuk iOS 11

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

Ini untuk jenis nama notifikasi keyboardWillShow. Jenis lain dapat dipilih dari opsi yang tersedia

Selector bertipe @objc func yang menangani bagaimana keyboard akan ditampilkan (ini adalah fungsi pengguna Anda)

Ashim Dahal
sumber
Hanya untuk memperjelas bagi siapa pun yang membaca jawaban ini: "Selector bertipe @objc func ..." berarti fungsi yang terkait dengannya #selectorharus dijelaskan @objc. Sebagai contoh: @objc func keyboardShow() { ... }Itu membuat saya berpikir sebentar di Swift 4!
leanne
0

Swift 5 & Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)
David.Chu.ca
sumber
0

Pengamat Pemberitahuan Swift 5

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
sumber