NSNotificationCenter addObserver в Swift


392

Як додати спостерігача в Swift до центру сповіщень за замовчуванням? Я намагаюся перенести цей рядок коду, який надсилає сповіщення про зміну рівня акумулятора.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];

Що ви конкретно запитуєте? Як працює селектор?
nschum

1
Я не усвідомлював, що тип "Selector" - це лише рядок у Swift. Ніякої згадки про це в документах.
Беррі Блю

Відповіді:


442

Це те саме, що API API Objective-C, але використовує синтаксис Swift.

Swift 4.2 та Swift 5:

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

Якщо ваш спостерігач не успадковує об'єкт Objective-C, вам слід встановити префікс свого методу @objc, щоб використовувати його як селектор.

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

Див. Посилання NSNotificationCenter Class , Взаємодія з API- адресами Objective-C


3
Дякую! Я не знав, як передати ім'я селектора у Свіфт.
Беррі Блю

14
@BerryBlue, чи працювало вищевказане рішення для вас? Я вважаю, що вам потрібно змінити "batteryLevelChanged" на "batteryLevelChanged:", якщо ваша функція приймає NSNotification як параметр.
Ольшанськ

1
@Olshansk Так, ви маєте рацію. Вам це потрібно. Дякую!
Беррі Блю

чому це UIDeviceBatteryLevelDidChangeNotificationне в лапках? Це рядковий тип.
kmiklas

13
Обов’язково коментуйте або клас, або цільовий метод @objc.
Клаас

757

Swift 4.0 та Xcode 9.0+:

Послати повідомлення (повідомлення):

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

АБО

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

Отримати (отримати) сповіщення:

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

Функція-обробник методу для отриманого повідомлення:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 та Xcode 8.0+:

Послати повідомлення (повідомлення):

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

Отримати (отримати) сповіщення:

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

Обробник методу для отриманого повідомлення:

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

Видалити сповіщення:

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

Swift 2.3 та Xcode 7:

Надсилання (повідомлення) повідомлення

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

Отримати (отримати) повідомлення

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

Обробник методів для отриманого повідомлення

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


Для історичних версій Xcode ...



Надсилання (повідомлення) повідомлення

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

Отримати (отримати) повідомлення

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

Видалити сповіщення

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

Обробник методів для отриманого повідомлення

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

Коментуйте або клас, або цільовий метод за допомогою @objc

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

// Or

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

21
Обов’язково коментуйте або клас, або цільовий метод @objc.
Клаас

1
@goofansu Ви впевнені? Я вважаю, що ви повинні додати його, коли це чистий клас Swift.
Клаас

10
methodOFReceivedNoticationповинно бути або анотовано, dynamicабо бути членом підкласу NSObject.
Клаас

1
Якщо ні, то отримую попередження про виконання object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Клаас

2
@TaylorAllred, дуже дякую за перегляд моєї відповіді. Я дуже ціную вашу пропозицію. Я це змінив. Перегляньте його.
Реніш Дадханія

46

Хороший спосіб зробити це - використовувати addObserver(forName:object:queue:using:)метод, а не addObserver(_:selector:name:object:)метод, який часто використовується з коду Objective-C. Перевага першого варіанту полягає в тому, що вам не доведеться використовувати @objcатрибут у своєму методі:

    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)

і ви можете навіть просто використовувати закриття замість методу, якщо хочете:

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

Ви можете використовувати повернене значення, щоб пізніше перестати слухати сповіщення:

    NotificationCenter.default.removeObserver(observer)

Була ще одна перевага використання цього методу, яка полягала в тому, що він не вимагає використання рядків селектора, які компілятор не міг статично перевірити, і тому вони були неміцними до розриву, якщо метод перейменований, але Swift 2.2 і пізніше включіть #selectorвирази, які вирішують цю проблему.


7
Це чудово! Для повноти я хотів би також побачити незареєструючий приклад. Зовсім інший, ніж addObserver(_:selector:name:object:) спосіб реєстрації. Ви повинні утримати об’єкт, повернутий addObserverForName(_:object:queue:usingBlock:)і передати йогоremoveObserver:
Лукас Гуссен

1
Це потребує оновлення, щоб включати в себе дереєстрацію об'єкта, що повернувся addObserverForName(_:object:queue:usingBlock:).
Гіпербола

3
Це набагато краща відповідь, ніж коннор або Реніш (обидва вище під час цього коментаря), оскільки уникнути необхідності використання методів Obj-C #selector. Результат набагато більш швидкий, і правильніше, ІМО. Дякую!
patr1ck

2
Пам'ятайте, що якщо ви використовуєте це в, скажімо, a UIViewControllerі посилаєтесь на selfце закриття, вам потрібно використовувати [weak self]або у вас буде еталонний цикл і витік пам'яті.
Роб N

40

Swift 3.0 в Xcode 8

Swift 3.0 замінив багато API (строго набрані) API на struct"типи обгортки", як це відбувається у NotificationCenter. Сповіщення тепер ідентифікуються struct Notfication.Nameскоріше, ніж самим String. Дивіться посібник з міграції на Swift 3 .

Попереднє використання:

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

Нове використання 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)

Усі типи системних сповіщень тепер визначаються як статичні константи на Notification.Name; тобто .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching,.UITextFieldTextDidChange і т.д.

Ви можете продовжити за Notification.Nameдопомогою власних користувальницьких сповіщень, щоб залишатися узгодженими із системними сповіщеннями:

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

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)

24
  1. Визначте ім’я сповіщення

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Ви можете додати спостерігача двома способами:

    Використання 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"]
    }

    або використовуючи 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. Опублікуйте своє повідомлення

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

від iOS 9 та OS X 10.11. Спостерігачеві NSNotificationCenter більше не потрібно скасовувати реєстрацію під час розміщення. більше інформації

Для blockосновної реалізації вам потрібно зробити слабкий сильний танець, якщо ви хочете використовувати selfвсередині блоку.більше інформації

Спостерігачів на основі блоків потрібно видалити більше інформації

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)

5
"від iOS 9 та OS X 10.11. Спостерігачеві NSNotificationCenter більше не потрібно реєструватися під час розміщення." Це справедливо лише для спостерігачів на основі селектора. Спостерігачів, що базуються на блоках, все ще потрібно усунути.
Абхінав

8

Передайте дані за допомогою NSNotificationCenter

Ви також можете передавати дані за допомогою NotificationCentre в swift 3.0 та NSNotificationCenter в swift 2.0.

Версія Swift 2.0

Передайте інформацію за допомогою userInfo, який є необов'язковим словником типу [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

Тепер користувачInfo приймає [AnyHashable: Any]? як аргумент, який ми пропонуємо як буквальний словник у 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   
  }
}

Дані проходження джерела за допомогою NotificationCentre (swift 3.0) та NSNotificationCenter (swift 2.0)


Радий почути, що це допомогло тобі :)
Сахіль

6

У Swift 5

Скажімо, якщо ви хочете отримувати дані від ViewControllerB до ViewControllerA

ViewControllerA (приймач)

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 (відправник)

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

}

2

Я можу виконати одне з наступних дій, щоб успішно використовувати селектор - не коментуючи нічого за допомогою @objc:

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

АБО

let notificationSelector: Selector = "batteryLevelChanged:"

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

У моїй версії xcrun показано Swift 1.2, і це працює на Xcode 6.4 та Xcode 7 beta 2 (що я думав, що буде використовувати Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)

Вам не потрібно коментувати, @objcякщо ваш клас спостерігача успадковує від NSObject.
Антоніо Фавата

І вам не потрібно явно кинути Stringв Selectorбудь-який. :)
Антоніо Фавата

@alfvata: Мій клас спостерігача не успадковує від NSObject. Він успадковує від AnyObject, стилю Swift. Явне закидання рядка до Selector дозволяє мені уникати будь-якого іншого обходу, пов'язаного з Objective-C.
leanne

Я не впевнений, що розумію, як це працює. Я видалив @objcанотацію з методу в моєму NSObjectкласі, який не спостерігав, додав as Selectorкастинг до імені Stringселектора, і коли сповіщення запускається, програма виходить з ладу. Моя версія Swift точно така ж, як і ваша.
Антоніо Фавата

3
@alfavata, я не знаю, що тобі сказати. Зараз я перебуваю на Xcode Beta 4, і він все ще працює. Мій проект повністю Swift; немає компонентів Objective-C. Можливо, це має значення. Можливо, в налаштуваннях проекту є щось інше. Існує будь-яка кількість можливостей! Я скажу: доки @objcанотація працює на вас, а цього не відбувається, продовжуйте коментувати!
leanne

2

У швидкому 2.2 - XCode 7.3 ми використовуємо #selectorдляNSNotificationCenter

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

2

Ми також повинні видалити сповіщення.

Вих.

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

}

2
Я вважаю, що вам це не потрібно з iOS 9. Це робиться автоматично.
Віктор Кучера

1

У швидкому 3, Xcode 8.2: - перевірка рівня акумулятора

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

1

NSNotificationCenter додайте синтаксис спостерігача в Swift 4.0 для iOS 11

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

Це для типу імені сповіщення на клавіатурі WillShow. Інший тип можна вибрати з доступної опції

селектор типу @objc func, який обробляє показ клавіатури (це ваша функція користувача)


Просто для уточнення для тих, хто читає цю відповідь: "Селектор типу @objc func ..." означає, що функція, пов'язана з, #selectorповинна бути помічена @objc. Наприклад: @objc func keyboardShow() { ... }Це кинуло мене на хвилину у Swift 4!
leanne

0

Swift 5 та Xcode 10.2:

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

0

Швидкий 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)

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.