Ответы:
Вы также можете использовать протокол для этого
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 {
Есть более чистый (я думаю) способ добиться этого
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
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)
}
Это немного странно, поскольку я ожидал, что это будет Enum, а не какая-то настраиваемая структура, которая, по-видимому, не имеет больше преимуществ.
В Notification for NSNotification.Name есть typealias:
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
было перечисление, никто бы не смог определять новые уведомления. Мы используем структуры для типов, подобных перечислению, которые должны разрешать добавление новых членов. (См. Предложение о быстрой эволюции .)
Notification
это тип значения (а структура), так что он может извлечь выгоду из семантики Свифта для значения (им) изменчивости. Как правило, типы Foundation удаляют свои "NS" в Swift 3, но там, где существует один из новых типов значений Foundation для его замены, старый ссылочный тип остается (сохраняя имя "NS"), так что вы все еще можете использовать его, когда вам нужна эталонная семантика или ее подкласс. Смотрите предложение .
Более простой способ:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
Вы можете добавить собственный инициализатор в 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)
case
нижнем регистре должны быть только s в перечислении, а не само перечисление. Имена типов пишутся в верхнем регистре, а перечисления - это типы.
Я могу предложить другой вариант, аналогичный тому, что предлагал @CesarVarela.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Это позволит вам легко публиковать и подписываться на уведомления.
NotificationCenter.default.post(Notification(name: .notificationName))
Надеюсь, что это поможет вам.
Я сделал свою собственную реализацию, смешивая вещи оттуда и там, и считаю это наиболее удобным. Поделитесь тем, кто может быть заинтересован:
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)
}
}
NSNotification.Name(rawValue: "myNotificationName")
Это просто ссылка
// 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)
Хотя это не связано с вопросом, то же самое можно сделать с сегментами раскадровки, чтобы не вводить строки в кавычках:
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
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)