В Objective-C користувацьке сповіщення - це просто звичайна NSString, але в WWDC-версії Swift 3 це не очевидно, якою вона повинна бути.
В Objective-C користувацьке сповіщення - це просто звичайна NSString, але в WWDC-версії Swift 3 це не очевидно, якою вона повинна бути.
Відповіді:
Ви також можете використовувати для цього протокол
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)
Таким чином імена сповіщень будуть зняті з фонду 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, а не якась спеціальна структура, яка, здавалося б, не має більше користі.
Існує типізація в повідомленні для NSNotification.Name:
public typealias Name = NSNotification.Name
Заплутаною частиною є те, що як повідомлення, так і NSNotification існують у Swift
Отже, щоб визначити власне власне сповіщення, зробіть щось таке:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Тоді щоб зателефонувати:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Notification.Name
перерахунок, ніхто не зміг би визначити нові сповіщення. Ми використовуємо структури для інакше перелічених типів, яким потрібно додати нових членів. (Див . Пропозицію про швидку еволюцію .)
Notification
це тип значення (структура), так що він може отримати вигоду з семантики Swift для значення (im) змінності. Як правило, типи фонду скидають свої "NS" у Swift 3, але там, де існує один із нових типів Value 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 у enum має бути нижчим регістром, а не сам enum. Назви типів мають великі літери, а перерахунки - типи.
Я можу запропонувати інший варіант, подібний до запропонованого @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)
}
}
Це просто довідка
// 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)
Перевага використання enums полягає в тому, що ми отримуємо компілятор, щоб перевірити правильність імені. Зменшує потенційні проблеми та спрощує рефакторинг.
Для тих, хто любить використовувати переписки замість цитованих рядків для імен сповіщень, цей код робить фокус:
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];
А у Свіфта (5) так:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)