【问题标题】:NSNotificationCenter addObserver in SwiftSwift 中的 NSNotificationCenter addObserver
【发布时间】:2014-07-25 18:53:30
【问题描述】:

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

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

【问题讨论】:

  • 你具体要问什么?选择器是如何工作的?
  • 我没有意识到“选择器”类型在 Swift 中只是一个字符串。文档中没有提到它。

标签: ios swift nsnotificationcenter


【解决方案1】:

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
}

【讨论】:

  • 确保使用@objc注释类或目标方法。
  • @goofansu 你确定吗?我认为当它是一个纯 Swift 类时你必须添加它。
  • methodOFReceivedNotication 必须使用dynamic 进行注释,或者是 NSObject 子类的成员。
  • 如果没有,我会收到运行时警告 object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead, Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
  • @TaylorAllred,非常感谢您查看我的回答。我非常感谢您的建议。我已经改变了。请检查它。
【解决方案2】:

与 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 Class ReferenceInteracting with Objective-C APIs

【讨论】:

  • @BerryBlue,上述解决方案对您有用吗?如果您的函数接受 NSNotification 作为参数,我相信您需要将“batteryLevelChanged”更改为“batteryLevelChanged:”。
  • 为什么UIDeviceBatteryLevelDidChangeNotification 不在引号中?是字符串类型。
  • @kmiklas 如果需要,您可以将其放在引号中。 UIDeviceBatteryLevelDidChangeNotification 是“UIDeviceBatteryLevelDidChangeNotification”的 NSString。无论哪种方式,您都会得到相同的结果
  • @connor ..直到 Apple 决定更改底层字符串值。使用 Apple 给你的字符串常量。
  • 确保使用@objc注释类或目标方法。
【解决方案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 expressions

【讨论】:

  • 这太棒了!为了完整起见,我也想看看取消注册的例子。这与addObserver(_:selector:name:object:) 的注销方式完全不同。您必须保留addObserverForName(_:object:queue:usingBlock:) 返回的对象并将其传递给removeObserver:
  • 这需要更新以包括取消注册 addObserverForName(_:object:queue:usingBlock:) 返回的对象。
  • 这是一个比 connor 或 Renish 更好的答案(在此评论时都在上面),因为它不必使用 Obj-C #selector 方法。结果是更加 Swift-y 和更正确,IMO。谢谢!
  • 记住,如果你在 UIViewController 中使用它并在那个闭包中引用 self,你需要使用 [weak self] 否则你会有一个引用循环和内存泄漏。
【解决方案4】:
  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 观察者在被取消注册时 解除分配。 more info

对于基于block 的实现,如果您想在块内使用self,则需要进行弱强舞蹈。 more info

需要移除基于块的观察者more info

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

【讨论】:

  • “来自 iOS 9 和 OS X 10.11。NSNotificationCenter 观察者在被释放时不再需要注销自身。” 这仅适用于选择器基于观察者。仍然需要删除基于块的观察者。
  • 如果块中只有一行代码,你就不需要做弱强之舞。您可以只使用 self?.myFunction 之类的弱项。好吧,ObjC 就是这种情况,我认为它在 Swift 中也是如此。
  • @Abhinav 他们怎么会有这样的差异?这很奇怪。
【解决方案5】:

Xcode 8 中的 Swift 3.0

Swift 3.0 已经用struct“包装器类型”替换了许多“字符串类型”API,NotificationCenter 就是这种情况。通知现在由struct Notfication.Name 而不是String 标识。请参阅Migrating to Swift 3 guide

以前的用法:

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

【讨论】:

    【解决方案6】:

    使用 NSNotificationCenter 传递数据

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

    Swift 2.0 版本

    使用 [NSObject : AnyObject] 类型的可选字典 use​​rInfo 传递信息?

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

    来源pass data using NotificationCentre(swift 3.0) and NSNotificationCenter(swift 2.0)

    【讨论】:

    • 很高兴听到它对您有所帮助 :)
    【解决方案7】:

    Swift 5

    假设如果想从 ViewControllerB 接收数据到 视图控制器A

    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】:

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

      【讨论】:

        【解决方案9】:

        我能够执行以下操作之一来成功使用选择器 - 无需使用 @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)
        

        【讨论】:

        • 如果您的观察者类继承自NSObject,则无需使用@objc 进行注释。
        • 而且您也不需要将String 显式转换为Selector。 :)
        • @alfvata:我的观察者类没有继承自 NSObject。它继承自 AnyObject,Swift 风格。将字符串显式转换为 Selector 可以让我避免执行任何其他与 Objective-C 相关的解决方法。
        • 我不确定我是否理解它是如何工作的。我从我的非NSObject 观察者类的方法中删除了@objc 注释,将as Selector 转换添加到String 选择器名称,当通知触发时应用程序崩溃。我的 Swift 版本和你的完全一样。
        • @alfavata,我不知道该告诉你什么。我现在在 Xcode Beta 4 上,它仍在工作。我的项目完全是 Swift 的;没有 Objective-C 组件。也许这会有所作为。也许项目设置中有一些不同。有多种可能性!我会说:只要 @objc 注释对你有用,而这种方式不起作用,那么继续注释!
        【解决方案10】:

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

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

        【讨论】:

          【解决方案11】:

          我们也应该删除通知。

          例如

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

          【讨论】:

          • 我相信你从 iOS 9 开始就不需要这个了。它是自动完成的。
          【解决方案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】:

            NSNotificationCenterSwift 4.0 中为 iOS 11 添加观察者语法

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

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

            Selector 是 @objc func 类型,它处理键盘的显示方式(这是你的用户函数)

            【讨论】:

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

            Swift 5 和 Xcode 10.2:

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

            【讨论】:

              【解决方案15】:

              这是自定义通知观察者和发布的非常简单的示例

              添加通知观察者

              NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: Notification.Name("CustomeNotificationName"), object: nil)
              

              添加 Selector 并处理 Observer 调用

              @objc func myFunction(notification: Notification) {
                  
                  //Write you code
              }
              

              在需要时发布通知(观察者)。

              NotificationCenter.default.post(name: NSNotification.Name("CustomeNotificationName"), object: "Object", userInfo: ["key":"Value"])
              

              注意:- 离开屏幕时创建用户,您需要删除观察者。例如

              override func viewWillDisappear(_ animated: Bool) {
                  super.viewWillDisappear(animated)
                  NotificationCenter.default.removeObserver(self);
              }
              

              【讨论】:

                猜你喜欢
                • 2012-12-07
                • 1970-01-01
                • 1970-01-01
                • 2014-11-22
                • 1970-01-01
                • 1970-01-01
                • 2015-05-01
                • 1970-01-01
                • 2023-03-28
                相关资源
                最近更新 更多