NSNotificationCenter addObserver no Swift


392

Como você adiciona um observador no Swift ao centro de notificação padrão? Estou tentando portar essa linha de código que envia uma notificação quando o nível da bateria muda.

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

O que você está perguntando especificamente? Como o seletor funciona?
— Nschum 04/06

11
Eu não sabia que o tipo "Selector" é apenas uma string no Swift. Nenhuma menção a isso nos documentos.
— Berry Blue

Respostas:


443

É o mesmo que a API do Objective-C, mas usa a sintaxe do Swift.

Swift 4.2 e Swift 5:

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

Se o seu observador não herdar de um objeto Objective-C, você deve prefixar seu método @objcpara usá-lo como seletor.

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

Consulte Referência da classe NSNotificationCenter , Interagindo com APIs do Objective-C


3
Obrigado! Eu não sabia como passar o nome do seletor no Swift.
— Berry Blue

14
@BerryBlue, a solução acima funcionou para você? Acredito que você precise alterar "batteryLevelChanged" para "batteryLevelChanged:" se sua função aceitar a NSNotification como parâmetro.
— Olshansk

11
@Olshansk Sim, você está correto. Você precisa disso. Obrigado!
— Berry Azul

por que UIDeviceBatteryLevelDidChangeNotificationnão está entre aspas? É um tipo de string.
— kmiklas

13
Anote a classe ou o método de destino com @objc.
— Klaas

757

Swift 4.0 e Xcode 9.0+:

Enviar (Post) Notificação:

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

OU

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

Receber (Obter) Notificação:

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

Manipulador de método de função para notificação recebida:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 e Xcode 8.0 ou superior:

Enviar (Post) Notificação:

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

Receber (Obter) Notificação:

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

Manipulador de método para a notificação recebida:

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

Remover notificação:

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

Swift 2.3 e Xcode 7:

Enviar (Post) Notificação

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

Receber (receber) notificação

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

Manipulador de método para a notificação recebida

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


Para versões históricas do Xcode ...



Enviar (Post) Notificação

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

Receber (receber) notificação

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

Remover notificação

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

Manipulador de método para a notificação recebida

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

Anote a classe ou o método de destino com @objc

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

// Or

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

21
Anote a classe ou o método de destino com @objc.
— Klaas

11
@goofansu Você tem certeza? Eu acho que você precisa adicioná-lo quando é uma classe Swift pura.
— Klaas

10
methodOFReceivedNoticationdeve ser anotado dynamicou ser membro de uma subclasse de NSObject.
— Klaas

11
Se não, eu recebo um aviso de tempo de execução object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead, #Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
— Klaas

2
@TaylorAllred, Muito obrigado por revisar minha resposta. Eu realmente aprecio sua sugestão. Eu mudei isso. Por favor, revise-o.
— Renish Dadhaniya

46

Uma boa maneira de fazer isso é usar o addObserver(forName:object:queue:using:)método em vez do addObserver(_:selector:name:object:)método geralmente usado no código Objective-C. A vantagem da primeira variante é que você não precisa usar o @objcatributo no seu método:

    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)

e você pode até usar um fechamento em vez de um método, se quiser:

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

Você pode usar o valor retornado para parar de receber a notificação posteriormente:

    NotificationCenter.default.removeObserver(observer)

Costumava haver outra vantagem no uso desse método, que não exigia o uso de seqüências seletoras que não pudessem ser verificadas estaticamente pelo compilador e, portanto, eram frágeis para serem quebradas se o método fosse renomeado, mas Swift 2.2 e depois, inclua #selectorexpressões que corrigem esse problema.


7
Isso é ótimo! Para completar, eu também gostaria de ver um exemplo de cancelamento de registro. É bem diferente da addObserver(_:selector:name:object:) maneira de cancelar o registro. Você tem que manter o objeto retornado por addObserverForName(_:object:queue:usingBlock:)e passá-lo pararemoveObserver:
— Lucas Goossen

11
É necessário atualizar para incluir o cancelamento do registro do objeto retornado por addObserverForName(_:object:queue:usingBlock:).
— precisa

3
Essa é uma resposta muito melhor do que a de Connor ou Renish (ambas acima no momento deste comentário), porque é necessário usar os métodos Obj-C #selector. O resultado é muito mais rápido e correto, IMO. Obrigado!
— patr1ck

2
Lembre-se, se você usar isso em, digamos, a UIViewControllere se referir a selfesse fechamento, precisará usar [weak self]ou terá um ciclo de referência e vazamento de memória.
— Rob N

40

Swift 3.0 no Xcode 8

O Swift 3.0 substituiu muitas APIs struct"do tipo string" por "tipos de wrapper", como é o caso do NotificationCenter. As notificações agora são identificadas por um struct Notfication.Namee não por String. Consulte o guia Migrando para o Swift 3 .

Uso anterior :

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

Novo uso do 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)

Todos os tipos de notificação do sistema agora são definidos como constantes estáticas ativadas Notification.Name; ou seja .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, etc.

Você pode estender Notification.Namesuas próprias notificações personalizadas para permanecer consistente com as notificações do sistema:

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

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

24
  1. Declarar um nome de notificação

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Você pode adicionar observador de duas maneiras:

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

    ou usando 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. Publique sua notificação

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

do iOS 9 e OS X 10.11. Não é mais necessário que um observador do NSNotificationCenter se cancele o registro ao ser desalocado. mais informações

Para block implementação baseada, você precisa fazer uma dança fraco-forte se quiser usar selfdentro do bloco. mais informações

Observadores baseados em blocos precisam ser removidos mais informações

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

5
"a partir do iOS 9 e OS X 10.11. Não é mais necessário que um observador do NSNotificationCenter se cancele o registro ao ser desalocado." Isso é verdade apenas para os observadores baseados no Seletor. Observadores baseados em blocos ainda precisam ser removidos.
— Abhinav

8

Passar dados usando o NSNotificationCenter

Você também pode transmitir dados usando o NotificationCentre no swift 3.0 e o NSNotificationCenter no swift 2.0.

Versão Swift 2.0

Passar informações usando userInfo, que é um dicionário opcional do tipo [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   
  }
}

Versão Swift 3.0

O userInfo agora usa [AnyHashable: Any]? como argumento, que fornecemos como um literal de dicionário no 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   
  }
}

Dados da passagem de origem usando o NotificationCentre (swift 3.0) e o NSNotificationCenter (swift 2.0)


Fico feliz em ouvir isso ajudou você :)
— Sahil

6

No Swift 5

Digamos que, se desejar receber dados do ViewControllerB para o ViewControllerA

ViewControllerA (Receiver)

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

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

Posso fazer o seguinte para usar com sucesso um seletor - sem fazer anotações em nada com @objc:

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

OU

let notificationSelector: Selector = "batteryLevelChanged:"

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

Minha versão do xcrun mostra o Swift 1.2, e isso funciona no Xcode 6.4 e no Xcode 7 beta 2 (que eu pensei que usaria o Swift 2.0):

$xcrun swift --version

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

Você não precisa fazer anotações @objcse sua classe de observador herdar NSObject.
— Antonio Favata

E você também não precisa converter explicitamente um Stringpara Selector. :)
— Antonio Favata

@ alfvata: Minha classe de observador não herda de NSObject. Herda de AnyObject, estilo Swift. A conversão explícita da string para o Selector está me permitindo evitar qualquer outra solução alternativa relacionada ao Objective-C.
— leanne

Não sei se entendi como isso funciona. Eu removi a @objcanotação do método na minha NSObjectclasse de não observador, adicionei a as Selectorconversão ao Stringnome do seletor e, quando a notificação é acionada, o aplicativo falha. Minha versão do Swift é exatamente igual à sua.
— Antonio Favata 10/07

3
@ alfavata, não sei o que dizer. Agora estou no Xcode Beta 4 e ainda está funcionando. Meu projeto é totalmente rápido; não há componentes Objective-C. Talvez isso faça a diferença. Talvez haja algo diferente nas configurações do projeto. Há inúmeras possibilidades! Eu direi: contanto que a @objcanotação funcione para você, e dessa maneira não, continue anotando!
— Leanne

2

No swift 2.2 - XCode 7.3, usamos #selectorparaNSNotificationCenter

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

2

Também devemos remover a notificação.

Ex.

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

}

2
Eu acredito que você não precisa disso desde o iOS 9. É feito automaticamente.
— Viktor Kucera

1

No swift 3, Xcode 8.2: - verificação do nível do estado da bateria

//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 adiciona sintaxe de observador no Swift 4.0 para iOS 11

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

É para o tipo de nome de notificação keyboardWillShow. Outro tipo pode ser selecionado na opção disponível

o seletor é do tipo @objc func, que lida com a aparência do teclado (essa é a sua função de usuário)


Apenas para esclarecer quem estiver lendo esta resposta: "o Seletor é do tipo @objc func ..." significa que a função associada #selectordeve ser anotada @objc. Por exemplo: @objc func keyboardShow() { ... }Isso me impressionou por um minuto no Swift 4!
— Leanne

0

Swift 5 e Xcode 10.2:

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

0

Observador de notificações 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)

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.