Swift中的NSNotificationCenter addObserver


392

如何在Swift中将观察者添加到默认通知中心?我正在尝试移植此行代码,以便在电池电量变化时发送通知。

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

您具体要问什么?选择器如何工作?
nschum 2014年

1
我没有意识到“选择器”类型只是Swift中的一个字符串。在文档中没有提及它。
Berry Blue

Answers:


442

与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类参考与Objective-C API交互


3
谢谢!我不知道如何在Swift中传递选择器名称。
Berry Blue

14
@BerryBlue,以上解决方案对您有用吗?我相信,如果您的函数接受NSNotification作为参数,则需要将“ batteryLevelChanged”更改为“ batteryLevelChanged:”。
Olshansk 2014年

1
@Olshansk是的,您是对的。您确实需要。谢谢!
Berry Blue

为什么UIDeviceBatteryLevelDidChangeNotification不在引号中?这是一个字符串类型。
kmiklas 2014年

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类中添加它。
克拉斯2014年

10
methodOFReceivedNotication必须使用dynamicNSObject 注释或为NSObject的子类的成员。
克拉斯2014年

1
如果没有,我会收到运行时警告object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble aheadUnrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas 2014年

2
@TaylorAllred,非常感谢您查看我的回答。非常感谢您的建议。我已经改变了。请检查一下。
Renish Dadhaniya 2015年

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:
Lucas Goossen,2016年

1
这需要更新以包括注销由所返回的对象addObserverForName(_:object:queue:usingBlock:)
Hyperbole

3
这是一个比connor或Renish的答案更好的答案(在本文发表时均为以上),因为它可以避免使用Obj-C #selector方法。结果是,IMO更加迅速且正确。谢谢!
patr1ck '16

2
记住,如果在闭包中使用a UIViewController并引用self它,则需要使用它,[weak self]否则将有一个参考周期和内存泄漏。
罗布N

40

Xcode 8中的Swift 3.0

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)

现在,所有系统通知类型都被定义为on上的静态常量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观察者不再需要注销自身。” 这仅适用于基于选择器的观察者。基于块的观察者仍然需要删除。
Abhinav

8

使用NSNotificationCenter传递数据

您还可以使用swift 3.0中的NotificationCentre和swift 2.0中的NSNotificationCenter传递数据。

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

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
Antonio Favata 2015年

而且您不需要显式地将a强制String转换Selector为。:)
Antonio Favata

@alfvata:我的观察者类不继承自NSObject。它继承自Swift风格的AnyObject。将字符串显式转换为Selector可以避免执行其他与Objective-C相关的其他解决方法。
leanne 2015年

我不确定我是否了解它的工作原理。我@objc从非NSObject观察者类的方法中删除了注释,将as Selector强制类型转换添加到了String选择器名称中,并且当通知触发应用程序崩溃时。我的Swift版本与您的完全相同。
Antonio Favata

3
@alfavata,我不知道该怎么说。我现在在使用Xcode Beta 4,并且仍在工作。我的项目完全是Swift。没有Objective-C组件。也许会有所作为。也许项目设置中有些不同。有许多可能性!我会说:只要@objc注释对您有用,而这种方式不起作用,请继续注释!
leanne

2

在Swift 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

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

1

NSNotificationCenteriOS 4.0Swift 4.0中添加观察者语法

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

这是用于keyboardWillShow通知名称类型。可以从可用选项中选择其他类型

选择器的类型为@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

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)

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.