NSNotificationCenter addObserver в Swift

342

Как добавить наблюдателя в Swift в центр уведомлений по умолчанию? Я пытаюсь портировать эту строку кода, которая отправляет уведомление при изменении уровня заряда батареи.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
  • 0
    Что конкретно ты спрашиваешь? Как работает селектор?
  • 1
    Я не осознавал, что тип «Селектор» - это просто строка в Swift. Никаких упоминаний об этом в документах.
Показать ещё 1 комментарий
Теги:
nsnotificationcenter

12 ответов

427
Лучший ответ

Он такой же, как Objective-C API, но использует синтаксис Swift.

NSNotificationCenter.defaultCenter().addObserver(
    self,
    selector: #selector(batteryLevelChanged:),
    name: UIDeviceBatteryLevelDidChangeNotification,
    object: nil)

Свифт 3:

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

Swift 4.2:

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

Если ваш наблюдатель не наследует объект Objective-C, вы должны @objc префикс @objc к своему методу, чтобы использовать его в качестве селектора.

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

Посмотрите Ссылку класса NSNotificationCenter, Взаимодействие с API Objective C

  • 3
    Спасибо! Я не знал, как передать имя селектора в Swift.
  • 14
    @BerryBlue, вышеприведенное решение сработало для вас? Я считаю, что вам нужно изменить «batteryLevelChanged» на «batteryLevelChanged:», если ваша функция принимает NSNotification в качестве параметра.
Показать ещё 15 комментариев
741

Swift 4.0 и Xcode 9. 0+:

Отправить (Post) уведомление:

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+:

Отправить (Post) уведомление:

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:

Отправить (Post) уведомление

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



Отправить (Post) уведомление

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
}
  • 19
    Обязательно аннотируйте класс или целевой метод с помощью @objc .
  • 0
    @Klaas Не нужно добавлять @objc
Показать ещё 26 комментариев
42

Swift 3.0 в Xcode 8

Swift 3.0 заменил многие "строчно типизированные" API с struct "типами обертки", как в случае с NotificationCenter. Уведомления теперь идентифицируются с помощью struct Notfication.Name, а не String. См. Миграция в Swift 3 guide.

Предыдущее использование:

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

Хорошим способом сделать это является использование 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 выражения, которые исправляют эту проблему.

  • 6
    Это замечательно! Для полноты картины я бы тоже хотел увидеть незарегистрированный пример. Это совсем другой, чем способ отмены регистрации addObserver(_:selector:name:object:) . Вы должны сохранить объект, возвращенный addObserverForName(_:object:queue:usingBlock:) и передать его в removeObserver:
  • 1
    Это требует обновления, чтобы включить addObserverForName(_:object:queue:usingBlock:) регистрации возражений, возвращаемых addObserverForName(_:object:queue:usingBlock:) .
Показать ещё 2 комментария
16
  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(notificaiont: Notification) {
        print(notificaiont.object ?? "") //myObject
        print(notificaiont.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(notificaiont: notification)
    }
    
    func myFunction(notificaiont: Notification) {
        print(notificaiont.object ?? "") //myObject
        print(notificaiont.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }
    
  3. Разместите ваше уведомление

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

от iOS 9 и OS X 10.11. Больше не нужно, чтобы наблюдатель NSNotificationCenter отменял свою регистрацию при освобождении. больше информации

Для реализации, основанной на block вам нужно танцевать слабо-сильный танец, если вы хотите использовать self внутри блока. больше информации

  • 1
    «из iOS 9 и OS X 10.11. Теперь наблюдателю NSNotificationCenter больше не нужно отменять регистрацию, когда его освобождают». Это верно только для наблюдателей на основе селектора. Наблюдатели на основе блоков все еще должны быть удалены.
7

Передача данных с использованием 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

Пользовательская информация теперь принимает [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)

  • 2
    большое спасибо мужчина!
  • 0
    Рад слышать, что это помогло вам :)
2

Мы также должны удалить уведомление.

Исх.

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

}
  • 1
    Я считаю, что вам не нужно это с iOS 9. Это делается автоматически.
2

В быстром 2.2 - XCode 7.3 мы используем #selector для NSNotificationCenter

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)
  • 0
    Понижающие голоса должны быть объяснены: x
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)
  • 0
    Вам не нужно аннотировать с помощью @objc если ваш класс наблюдателя наследуется от NSObject .
  • 0
    И вам не нужно явно приводить String к Selector . :)
Показать ещё 6 комментариев
1

NSNotificationCenter добавить синтаксис наблюдателя в Swift 4.0 для iOS 11

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

Это для типа имени типа keyboardWillShow. Другой тип можно выбрать из доступной опции

Селектор имеет тип @objc func, который обрабатывает клавиатуру (это ваша пользовательская функция)

  • 0
    Просто чтобы уточнить, кто читает этот ответ: «Селектор имеет тип @objc func ...» означает, что функция, связанная с #selector должна быть аннотирована @objc . Например: @objc func keyboardShow() { ... } Это бросило меня на минуту в Swift 4!
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
    }
0

Swift 5 & Xcode 10.2:

NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(batteryLevelChanged:),
name: UIDevice.batteryLevelDidChangeNotification,
object: nil)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню