In Objective-C, una notifica personalizzata è solo un semplice NSString, ma non è ovvio nella versione WWDC di Swift 3 proprio quello che dovrebbe essere.
In Objective-C, una notifica personalizzata è solo un semplice NSString, ma non è ovvio nella versione WWDC di Swift 3 proprio quello che dovrebbe essere.
Risposte:
Potresti anche usare un protocollo per questo
protocol NotificationName {
var name: Notification.Name { get }
}
extension RawRepresentable where RawValue == String, Self: NotificationName {
var name: Notification.Name {
get {
return Notification.Name(self.rawValue)
}
}
}
Quindi definisci i nomi delle notifiche come enum
ovunque desideri. Per esempio:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
E usalo come
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
In questo modo i nomi delle notifiche verranno disaccoppiati dalla Fondazione Notification.Name
. E dovrai solo modificare il tuo protocollo nel caso in cui l'implementazione per Notification.Name
modifiche.
NotificationName
modo che la name
proprietà venga aggiunta solo alle enumerazioni conformi al protocollo.
extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
C'è un modo più pulito (credo) per ottenerlo
extension Notification.Name {
static let onSelectedSkin = Notification.Name("on-selected-skin")
}
E poi puoi usarlo in questo modo
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
extension NSNotification.Name
invece di extension Notification.Name
. Altrimenti Swift 3 reclami con'Notification' is ambiguous for type lookup in this context
Notification.post è definito come:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
In Objective-C, il nome della notifica è un semplice NSString. In Swift, è definito come NSNotification.Name.
NSNotification.Name è definito come:
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
Questo è un po 'strano, dal momento che mi aspetterei che fosse un Enum e non una struttura personalizzata con apparentemente non più vantaggi.
C'è un typealias in Notification for NSNotification.Name:
public typealias Name = NSNotification.Name
La parte confusa è che in Swift esistono sia Notification che NSNotification
Quindi, per definire la tua notifica personalizzata, fai qualcosa come:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Quindi chiamarlo:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Notification.Name
fosse un enum, nessuno sarebbe in grado di definire nuove notifiche. Usiamo strutture per tipi simili a enum che devono consentire l'aggiunta di nuovi membri. (Vedi la proposta di evoluzione rapida .)
Notification
è un tipo di valore (uno struct), in modo che possa beneficiare della semantica di Swift per la mutabilità del valore (im). In genere, i tipi Foundation perdono il loro "NS" in Swift 3, ma dove esiste uno dei nuovi tipi di valore Foundation per soppiantarlo, il vecchio tipo di riferimento rimane in giro (mantenendo il nome "NS") in modo che tu possa ancora usarlo quando hai bisogno di semantica di riferimento o di sottoclassarla. Vedi la proposta .
Modo più semplice:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
Puoi aggiungere un inizializzatore personalizzato a NSNotification.Name
extension NSNotification.Name {
enum Notifications: String {
case foo, bar
}
init(_ value: Notifications) {
self = NSNotification.Name(value.rawValue)
}
}
Uso:
NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)
case
s in un enum dovrebbe essere minuscolo, non l'enum stesso. I nomi dei tipi sono in maiuscolo e le enumerazioni sono tipi.
Potrei suggerire un'altra opzione simile a quella suggerita da @CesarVarela.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Ciò ti consentirà di pubblicare e iscriverti facilmente alle notifiche.
NotificationCenter.default.post(Notification(name: .notificationName))
Spero che questo ti possa aiutare.
Ho realizzato la mia implementazione mescolando le cose da lì a lì e trovo che sia la più conveniente. Condivisione per chi potrebbe essere interessato:
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")
Questo è solo un riferimento
// 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)
Il vantaggio dell'utilizzo delle enumerazioni è che facciamo in modo che il compilatore controlli che il nome sia corretto. Riduce potenziali problemi e semplifica il refactoring.
Per coloro a cui piace usare le enumerazioni invece delle stringhe tra virgolette per i nomi delle notifiche, questo codice fa il trucco:
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)
}
}
Quindi puoi usarlo in questo modo:
NotificationCenter.default.post(.somethingHappened)
Sebbene non sia correlato alla domanda, lo stesso può essere fatto con i seguiti dello storyboard, per evitare di digitare stringhe tra virgolette:
enum StoryboardSegue: String {
case toHere
case toThere
case unwindToX
}
extension UIViewController {
func perform(segue: StoryboardSegue) {
performSegue(withIdentifier: segue.rawValue, sender: self)
}
}
Quindi, sul controller della vista, chiamalo come:
perform(segue: .unwindToX)
se utilizzi notifiche personalizzate di sola stringa, non c'è motivo di estendere le classi ma String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
Se vuoi che funzioni in modo pulito in un progetto che utilizza contemporaneamente Objective-C e Swift, ho trovato più facile creare le notifiche in Objective-C.
Crea un file .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";
Nel tuo MyProject-Bridging-Header.h
(dal nome del tuo progetto) per esporli a Swift.
#import "CustomNotifications.h"
Usa le tue notifiche in Objective-C in questo modo:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];
E in Swift (5) come questo:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)