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

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


评论

您具体问什么?选择器如何工作?

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

stackoverflow.com/questions/36910965 / ...

#1 楼

它与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

评论


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

–浆果蓝
2014年6月4日23:30

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

–奥尔尚斯克
2014年6月5日在2:46

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

–浆果蓝
2014年6月6日15:51



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

– kmiklas
14年6月18日在21:16

确保使用@objc注释类或目标方法。

–克拉斯
2014年8月24日23:32

#2 楼

Swift 4.0和Xcode 9.0+:

发送(发布)通知:

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


OR

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


接收(Get)通知:

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
}


评论


确保使用@objc注释类或目标方法。

–克拉斯
2014年8月24日23:24

@goofansu您确定吗?我认为您必须在纯Swift类中添加它。

–克拉斯
2014年12月30日上午10:25

methodOFReceivedNotication必须使用动态注释或为NSObject子类的成员。

–克拉斯
2014-12-30 13:37



如果没有,我得到类'TestNotifications.MyObject'的运行时警告对象0x7fd68852d710不实现methodSignatureForSelector:-麻烦,无法识别的选择器-[TestNotifications.MyObject methodOFReceivedNotication:]

–克拉斯
2014-12-30 13:38



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

– Renish Dadhaniya
2015年2月26日在4:12

#3 楼

一个很好的方法是使用addObserver(forName:object:queue:using:)方法,而不是Objective-C代码中经常使用的addObserver(_:selector:name:object:)方法。第一种变体的优点是您不必在方法上使用@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表达式。

评论


这很棒!为了完整起见,我也想看看注销示例。与addObserver(_:selector:name:object :)的注销方式完全不同。您必须保留由addObserverForName(_:object:queue:usingBlock :)返回的对象,并将其传递给removeObserver:

–卢卡斯·古森(Lucas Goossen)
16-2-26在13:57



这需要进行更新以包括对addObserverForName(_:object:queue:usingBlock :)返回的对象的注销。

–双曲线
16-3-29在16:55

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

–patr1ck
16年7月16日在17:41

记住,如果在UIViewController中使用此函数并在该闭包中引用self,则需要使用[weak self],否则将有一个参考周期和内存泄漏。

–罗布N
17-10-11在13:39



#4 楼

Xcode 8中的Swift 3.0

Swift 3.0已将struct“包装器类型”替换为许多“字符串类型” API,例如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)


#5 楼



声明通知名称

extension Notification.Name {
    static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
}



您可以通过两种方式添加观察者:

使用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"]
}



发布您的通知

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)


评论


“从iOS 9和OS X 10.11开始。在释放时,NSNotificationCenter观察者不再需要注销自身。”这仅适用于基于选择器的观察者。基于块的观察者仍然需要删除。

– Abhinav
18-11-20在4:04



如果块中只有一行代码,则无需进行弱强共舞。您可以只使用像self?.myFunction这样的弱项。好吧,ObjC就是这种情况,我认为在Swift中也是如此。

– Malhal
7月3日8:04



#6 楼

使用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
17年12月13日在17:11

#7 楼

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

}


#8 楼

我能够执行以下一项操作以成功使用选择器-无需使用@objc进行注释:

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


OR

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)


评论


如果观察者类继承自NSObject,则无需使用@objc进行注释。

– Antonio Favata
2015年7月9日在13:33



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

– Antonio Favata
2015年7月9日在13:33



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

–Leanne
2015年7月9日在22:00

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

– Antonio Favata
15年7月10日在9:50



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

–Leanne
15年7月27日在15:47

#9 楼

在Swift 2.2-XCode 7.3中,我们将#selector用于NSNotificationCenter

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


#10 楼

我们也应该删除通知。

例如。

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

}


评论


我相信自iOS 9以来您就不需要此功能。它是自动完成的。

– Viktor Kucera
17年9月25日在7:16

#11 楼

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)

}


#12 楼

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


#13 楼

NSNotificationCenter在iOS 4.0的Swift 4.0中添加了观察者语法

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


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

选择器为@objc func类型,用于处理键盘的显示方式(这是您的用户功能)

评论


只是为了向阅读此答案的任何人澄清:“选择器的类型为@objc函数...”意味着与#selector关联的函数必须使用@objc进行注释。例如:@objc func keyboardShow(){...}这使我在Swift 4中呆了一分钟!

–Leanne
17-10-17在22:29

#14 楼

Swift 5和Xcode 10.2:

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