NSNotificationCenter addObserver в Swift

393

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

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

Ответы:

443

Он такой же, как Objective-C API, но использует синтаксис 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 , Взаимодействие с API Objective C

Коннор
источник
3
Спасибо! Я не знал, как передать имя селектора в Swift.
Ягодный синий
14
@BerryBlue, вышеприведенное решение сработало для вас? Я считаю, что вам нужно изменить «batteryLevelChanged» на «batteryLevelChanged:», если ваша функция принимает NSNotification в качестве параметра.
Ольшанск
1
@ Ольшанск Да, вы правы. Вам это нужно. Спасибо!
Берри Блю
почему UIDeviceBatteryLevelDidChangeNotificationне в кавычках? Это строковый тип.
kmiklas
13
Обязательно пометьте либо класс, либо целевой метод с помощью @objc.
Клаас
757

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
}
Рениш Дадхания
источник
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. Результат гораздо более быстрый и более правильный, IMO. Спасибо!
patr1ck
2
Помните, что если вы используете это, скажем, в a UIViewControllerи ссылаетесь на selfэто замыкание, вам нужно его использовать, [weak self]иначе у вас будет цикл обращения и утечка памяти.
Роб N
40

Swift 3.0 в Xcode 8

Swift 3.0 заменил многие «строковые» 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)
Warif Akhand Rishi
источник
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

UserInfo теперь принимает [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)

Сахил
источник
Рад слышать, что это помогло вам :)
Sahil
6

В Свифт 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)
    }

}
swiftBoy
источник
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.
Линне
Я не уверен, что понимаю, как это работает. Я удалил @objcаннотацию из метода в своем NSObjectклассе, не являющемся наблюдателем, добавил as Selectorприведение к имени Stringселектора, и при запуске уведомления приложение вылетает. Моя версия Swift точно такая же, как ваша.
Антонио Фавата
3
@alfavata, я не знаю, что тебе сказать. Я сейчас на Xcode Beta 4, и он все еще работает. Мой проект полностью Swift; нет никаких компонентов Objective-C. Может быть, это имеет значение. Может быть, что-то другое в настройках проекта. Есть любое количество возможностей! Я скажу: пока @objcаннотация работает для вас, а этот путь - нет, продолжайте аннотировать!
Линн
2

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

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)
Дипак Тхакур
источник
2

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

Ex.

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

}
Панкадж Джангид
источник
2
Я считаю, что вам не нужно это с iOS 9. Это делается автоматически.
Виктор Кучера
1

В Swift 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
    }
Dhruv
источник
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!
Линн
0

Swift 5 & Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)
David.Chu.ca
источник
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)

}
Имран Рашид
источник