【发布时间】:2016-10-20 08:47:56
【问题描述】:
在 Objective-C 中,自定义通知只是一个普通的 NSString,但在 Swift 3 的 WWDC 版本中并不明显。
【问题讨论】:
标签: swift nsnotificationcenter nsnotifications nsnotification swift3
在 Objective-C 中,自定义通知只是一个普通的 NSString,但在 Swift 3 的 WWDC 版本中并不明显。
【问题讨论】:
标签: swift nsnotificationcenter nsnotifications nsnotification swift3
有一种更清洁的(我认为)方法来实现它
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
您也可以为此使用协议
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 属性仅添加到符合协议的枚举中。
extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
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)
【讨论】:
Notification.Name 是一个枚举,那么没有人能够定义新的通知。我们将结构用于需要允许添加新成员的其他类似枚举的类型。 (见swift-evolution proposal。)
Notification 是一个值类型(一个结构体),因此它可以从 Swift 的值语义中受益(im) 可变性。一般来说,Foundation 类型在 Swift 3 中删除了它们的“NS”,但是在存在一种新的 Foundation Value Types 来取代它的地方,旧的引用类型仍然存在(保留“NS”名称),因此您仍然可以在以下情况下使用它您需要引用语义或对其进行子类化。请参阅proposal。
更简单的方法:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
【讨论】:
我可能会建议另一个类似于@CesarVarela 建议的选项。
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
这将让您轻松发布和订阅通知。
NotificationCenter.default.post(Notification(name: .notificationName))
希望这会对你有所帮助。
【讨论】:
您可以向 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)
【讨论】:
cases 应该小写,而不是枚举本身。类型名称是大写的,枚举是类型。
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)
}
}
【讨论】:
仅供参考
// 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)
【讨论】:
使用枚举的好处是我们可以让编译器检查名称是否正确。减少潜在问题并使重构更容易。
对于那些喜欢使用枚举而不是引用字符串作为通知名称的人,这段代码可以解决问题:
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) 这会引发错误;您在扩展中添加的方法接受更多参数。
@CesarVarela 的回答很好,但是为了让代码稍微干净一些,你可以这样做:
extension Notification.Name {
typealias Name = Notification.Name
static let onSelectedSkin = Name("on-selected-skin")
static let onFoo = Name("on-foo")
}
【讨论】:
如果您使用纯字符串自定义通知,则没有理由扩展任何类,但 String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
【讨论】:
如果您希望它在同时使用 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)
【讨论】: