在 Objective-C 中,自定义通知只是一个普通的 NSString,但是在 Swift 3的 WWDC 版本中,通知应该是什么样子并不明显。
Notification.post 的定义是:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
在 Objective-C 中,通知名称是一个普通的 NSString。
名称定义为:
public struct Name : RawRepresentable, Equatable, Hashable, Comparable { public init(_ rawValue: String) public init(rawValue: String) }
这有点奇怪,因为我希望它是一个 Enum,而不是一些似乎没有更多好处的自定义结构。
在 Notification for NSNotification 中有一个类型化名称:
public typealias Name = NSNotification.Name
令人困惑的是,在 Swift 中同时存在通知和 NSNotification
因此,为了定义自己的自定义通知,可以这样做:
public class MyClass { static let myNotification = Notification.Name("myNotification") }
然后称之为:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
我认为有一个更干净的方法来实现它
extension Notification.Name { static let onSelectedSkin = Notification.Name("on-selected-skin") }
然后你可以像这样使用它
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
这只是参考
// 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)
可以向 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)
更简单的方法:
let name:NSNotification.Name = NSNotification.Name("notificationName") NotificationCenter.default.post(name: name, object: nil)
你也可以用一个协议来解决这个问题
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。例如:
enum
class MyClass { enum Notifications: String, NotificationName { case myNotification } }
然后把它当成
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
这样,通知名称将与 FoundationNotification.Name解耦。您只需要修改您的协议,以防 Notification.Name的实现发生变化。
Notification.Name
NSNotification.Name(rawValue: "myNotificationName")
我自己的实现混合了这里和那里的东西,发现这是最方便的。为感兴趣的人分享:
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) } }
使用枚举的优点是我们可以让编译器检查名称是否正确。减少潜在的问题,使重构更容易。
对于那些喜欢使用枚举而不是引号字符串作为通知名称的人来说,这段代码很有用:
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)
尽管与这个问题无关,但为了避免输入引用的字符串,故事板接续部分也可以这样做:
enum StoryboardSegue: String { case toHere case toThere case unwindToX } extension UIViewController { func perform(segue: StoryboardSegue) { performSegue(withIdentifier: segue.rawValue, sender: self) } }
然后,在视图控制器上,调用如下:
perform(segue: .unwindToX)
如果使用只有字符串的自定义通知,则没有理由扩展除 String以外的任何类
String
extension String { var notificationName : Notification.Name{ return Notification.Name.init(self) } }
@ CesarVarela 的回答很好,但是为了让代码更简洁一些,你可以这样做:
extension Notification.Name { typealias Name = Notification.Name static let onSelectedSkin = Name("on-selected-skin") static let onFoo = Name("on-foo") }
我可能会建议另一个类似于@CesarVarela 建议的选择。
extension Notification.Name { static var notificationName: Notification.Name { return .init("notificationName") } }
这将使您可以轻松地发布和订阅通知。
NotificationCenter.default.post(Notification(name: .notificationName))
希望这个能帮到你。
如果您希望在同时使用 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 公开它们。
MyProject-Bridging-Header.h
#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)