【问题标题】:How do you create custom notifications in Swift 3?如何在 Swift 3 中创建自定义通知?
【发布时间】:2016-10-20 08:47:56
【问题描述】:

在 Objective-C 中,自定义通知只是一个普通的 NSString,但在 Swift 3 的 WWDC 版本中并不明显。

【问题讨论】:

标签: swift nsnotificationcenter nsnotifications nsnotification swift3


【解决方案1】:

有一种更清洁的(我认为)方法来实现它

extension Notification.Name {

    static let onSelectedSkin = Notification.Name("on-selected-skin")
}

然后你就可以这样使用了

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)

【讨论】:

  • 我正在使用上面的代码。这是一个静态属性。
  • 很干净,我很喜欢
  • extension NSNotification.Name 而不是 extension Notification.Name 。否则 Swift 3 投诉 'Notification' is ambiguous for type lookup in this context
  • 你得到了我的支持,因为我在字符串中打错字,从而证明了输入通知名称的价值:P
  • 可能值得注意的是,这是 Apple 在 WWDC 2016 Session 207 developer.apple.com/videos/play/wwdc2016/207987654321@建议的方法
【解决方案2】:

您也可以为此使用协议

protocol NotificationName {
    var name: Notification.Name { get }
}

extension RawRepresentable where RawValue == String, Self: NotificationName {
    var name: Notification.Name {
        get {
            return Notification.Name(self.rawValue)
        }
    }
}

然后将您的通知名称定义为 enum 在您想要的任何位置。例如:

class MyClass {
    enum Notifications: String, NotificationName {
        case myNotification
    }
}

并像使用它

NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)

这样,通知名称将与 Foundation Notification.Name 分离。而且您只需要修改您的协议,以防Notification.Name 的实现发生变化。

【讨论】:

  • 这正是我最初认为它应该工作的方式 - 通知应该是枚举。谢谢你的伎俩!
  • 没问题!我编辑了代码以包含NotificationName 扩展的构造,因此name 属性仅添加到符合协议的枚举中。
  • 严格等价但更符合逻辑的 IMO,您可以像这样在 NotificationName(而不是 RawRepresentable)上定义扩展名:extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
【解决方案3】:

Notification.post 定义为:

public func post(name aName: NSNotification.Name, object anObject: AnyObject?)

在 Objective-C 中,通知名称是一个普通的 NSString。在 Swift 中,它被定义为 NSNotification.Name。

NSNotification.Name 定义为:

public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
    public init(_ rawValue: String)
    public init(rawValue: String)
}

这有点奇怪,因为我希望它是一个枚举,而不是一些似乎没有更多好处的自定义结构。

NSNotification.Name 的通知中有一个类型别名:

public typealias Name = NSNotification.Name

令人困惑的是,Swift 中同时存在 Notification 和 NSNotification

因此,为了定义您自己的自定义通知,请执行以下操作:

public class MyClass {
    static let myNotification = Notification.Name("myNotification")
}

然后调用它:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)

【讨论】:

  • 好答案。一些 cmets:这有点奇怪,因为我希望它是一个枚举 — 一个枚举是一个 封闭 集。如果Notification.Name 是一个枚举,那么没有人能够定义新的通知。我们将结构用于需要允许添加新成员的其他类似枚举的类型。 (见swift-evolution proposal。)
  • 令人困惑的是,Swift 中同时存在 Notification 和 NSNotificationNotification 是一个值类型(一个结构体),因此它可以从 Swift 的值语义中受益(im) 可变性。一般来说,Foundation 类型在 Swift 3 中删除了它们的“NS”,但是在存在一种新的 Foundation Value Types 来取代它的地方,旧的引用类型仍然存在(保留“NS”名称),因此您仍然可以在以下情况下使用它您需要引用语义或对其进行子类化。请参阅proposal
  • 让我澄清一下:我希望通知名称是枚举,就像错误一样。您可以定义自己的 Error 枚举,并使它们符合 ErrorType。
  • 是的——Apple 至少在理论上可以将 NotoficationName(或一些类似的)作为协议,您可以根据该协议创建符合要求的类型。我不知道,但他们没有这样做可能是有原因的……可能与 ObjC 桥接有关?如果您找到了更好的解决方案,请提交错误(至open source,Foundation Swift 已公开)。
  • 你可能是正确的,它应该以小写字母开头。
【解决方案4】:

更简单的方法:

let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)

【讨论】:

    【解决方案5】:

    我可能会建议另一个类似于@CesarVarela 建议的选项。

    extension Notification.Name {
        static var notificationName: Notification.Name {
            return .init("notificationName")
        }
    }
    

    这将让您轻松发布和订阅通知。

    NotificationCenter.default.post(Notification(name: .notificationName))
    

    希望这会对你有所帮助。

    【讨论】:

      【解决方案6】:

      您可以向 NSNotification.Name 添加自定义初始化程序

      extension NSNotification.Name {
          enum Notifications: String {
              case foo, bar
          }
          init(_ value: Notifications) {
              self = NSNotification.Name(value.rawValue)
          }
      }
      

      用法:

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

      【讨论】:

      • 小写 'enum type' 和 'init(_ type: type)' 用于 Swift 3.0.2
      • @Jalakoo 只有枚举中的cases 应该小写,而不是枚举本身。类型名称是大写的,枚举是类型。
      【解决方案7】:
      NSNotification.Name(rawValue: "myNotificationName")
      

      【讨论】:

      • Notification.Name("myNotificationName")
      【解决方案8】:

      我做了我自己的实现,混合了那里和那里的东西,发现这是最方便的。分享给任何可能感兴趣的人:

      public extension Notification {
          public class MyApp {
              public static let Something = Notification.Name("Notification.MyApp.Something")
          }
      }
      
      class ViewController: UIViewController {
          override func viewDidLoad() {
              super.viewDidLoad()
              NotificationCenter.default.addObserver(self,
                                                     selector: #selector(self.onSomethingChange(notification:)),
                                                     name: Notification.MyApp.Something,
                                                     object: nil)
          }
      
          deinit {
              NotificationCenter.default.removeObserver(self)
          }
      
          @IBAction func btnTapped(_ sender: UIButton) {
              NotificationCenter.default.post(name: Notification.MyApp.Something,
                                            object: self,
                                          userInfo: [Notification.MyApp.Something:"foo"])
          }
      
          func onSomethingChange(notification:NSNotification) {
              print("notification received")
              let userInfo = notification.userInfo!
              let key = Notification.MyApp.Something 
              let something = userInfo[key]! as! String //Yes, this works :)
              print(something)
          }
      }
      

      【讨论】:

        【解决方案9】:

        仅供参考

        // Add observer:
        NotificationCenter.default.addObserver(self,
            selector: #selector(notificationCallback),
            name: MyClass.myNotification,
            object: nil)
        
            // Post notification:
            let userInfo = ["foo": 1, "bar": "baz"] as [String: Any]
            NotificationCenter.default.post(name: MyClass.myNotification,
                object: nil,
                userInfo: userInfo)
        

        【讨论】:

          【解决方案10】:

          使用枚举的好处是我们可以让编译器检查名称是否正确。减少潜在问题并使重构更容易。

          对于那些喜欢使用枚举而不是引用字符串作为通知名称的人,这段代码可以解决问题:

          enum MyNotification: String {
              case somethingHappened
              case somethingElseHappened
              case anotherNotification
              case oneMore
          }
          
          extension NotificationCenter {
              func add(observer: Any, selector: Selector, 
                       notification: MyNotification, object: Any? = nil) {
                  addObserver(observer, selector: selector, 
                              name: Notification.Name(notification.rawValue),
                              object: object)
              }
              func post(notification: MyNotification, 
                        object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {
                  post(name: NSNotification.Name(rawValue: notification.rawValue), 
                       object: object, userInfo: userInfo)
              }
          }
          

          那么你可以这样使用它:

          NotificationCenter.default.post(.somethingHappened)
          

          虽然与问题无关,但同样可以使用情节提要 segues 来完成,以避免输入带引号的字符串:

          enum StoryboardSegue: String {
              case toHere
              case toThere
              case unwindToX
          }
          
          extension UIViewController {
              func perform(segue: StoryboardSegue) {
                  performSegue(withIdentifier: segue.rawValue, sender: self)
              }
          }
          

          然后,在您的视图控制器上,这样称呼它:

          perform(segue: .unwindToX)
          

          【讨论】:

          • > NotificationCenter.default.post(.somethingHappened) 这会引发错误;您在扩展中添加的方法接受更多参数。
          【解决方案11】:

          @CesarVarela 的回答很好,但是为了让代码稍微干净一些,你可以这样做:

          extension Notification.Name {
              typealias Name = Notification.Name
          
              static let onSelectedSkin = Name("on-selected-skin")
              static let onFoo = Name("on-foo")
          }
          

          【讨论】:

            【解决方案12】:

            如果您使用纯字符串自定义通知,则没有理由扩展任何类,但 String

                extension String {
                    var notificationName : Notification.Name{
                        return Notification.Name.init(self)
                    }
                }
            

            【讨论】:

              【解决方案13】:

              如果您希望它在同时使用 Objective-C 和 Swift 的项目中干净利落地工作,我发现在 Objective-C 中创建通知会更容易。

              创建一个 .m/.h 文件:

              //CustomNotifications.h
              #import <Foundation/Foundation.h>
              
              // Add all notifications here
              extern const NSNotificationName yourNotificationName;
              
              //CustomNotifications.m
              #import "CustomNotifications.h"
              
              // Add their string values here
              const NSNotificationName yourNotificationName = @"your_notification_as_string";
              

              在您的 MyProject-Bridging-Header.h(以您的项目命名)中将它们公开给 Swift。

              #import "CustomNotifications.h"
              

              像这样在 Objective-C 中使用通知:

              [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];
              

              在 Swift (5) 中是这样的:

              NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-06-12
                • 2015-12-25
                • 1970-01-01
                • 2014-02-09
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多