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];
Berry Blue
fonte
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

Connor
fonte
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
}
Renish Dadhaniya
fonte
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.

Jon Colverson
fonte
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)
Jeffrey Fulton
fonte
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)
Warif Akhand Rishi
fonte
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)

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

}
swiftBoy
fonte
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)
leanne
fonte
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)
Deepak Thakur
fonte
2

Também devemos remover a notificação.

Ex.

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

}
Pankaj Jangid
fonte
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
    }
Dhruv
fonte
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)

Ashim Dahal
fonte
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)
David.Chu.ca
fonte
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)

}
Imran Rasheed
fonte