In Objective-C, una notifica personalizzata è solo una semplice NSString, ma non è ovvio nella versione WWDC di Swift 3 proprio quello che dovrebbe essere.Come si creano notifiche personalizzate in Swift 3?
risposta
Si potrebbe 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)
}
}
}
E poi definire i nomi di notifica come enum
ovunque si desideri . Per esempio:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
e usarlo come
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
In questo modo i nomi di notifica saranno disaccoppiati dalla Fondazione Notification.Name
. E dovrai solo modificare il tuo protocollo nel caso in cui l'implementazione per le modifiche Notification.Name
.
Notification.post è definito come:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
in Objective-C, il nome di notifica è una pianura 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 aspetto che sia un Enum, e non qualche struct personalizzato con apparentemente non più beneficio.
C'è un typealias nella notifica per NSNotification.Name:
public typealias Name = NSNotification.Name
La parte confusa è che sia di notifica e NSNotification esistono in Swift
Quindi, al fine di definire il proprio notifiche personalizzate, fare somethine come:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Poi chiamarlo:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Buona risposta. Alcuni commenti: * Questo è un po 'strano, dal momento che mi aspetterei che sia un Enum * - Un enum è un insieme * chiuso *. Se "Notification.Name" fosse un enum, nessuno sarebbe in grado di definire nuove notifiche. Usiamo le strutture per i tipi di tipo enum che devono consentire l'aggiunta di nuovi membri. (Vedi la [proposta swift-evolution] (https://github.com/apple/swift-evolution/blob/master/proposals/0033-import-objc-constants.md).) – rickster
* La parte confusa è che entrambi Notifiche e NSNotification esistono in Swift * - 'Notification' è un tipo di valore (una struct), in modo che possa beneficiare della semantica di Swift per la mutabilità value (im). Generalmente, i tipi di Foundation abbandonano il loro "NS" in Swift 3, ma dove esiste uno dei nuovi Value Type Foundation per soppiantarlo, il vecchio tipo di riferimento rimane intorno (mantenendo il nome "NS") in modo che tu possa ancora usarlo quando hai bisogno di semantica di riferimento o di sottoclasse. Vedi la [proposta] (https://github.com/apple/swift-evolution/blob/master/proposals/0069-swift-mutability-for-foundation.md). – rickster
Vorrei chiarire: mi aspetto che i nomi delle notifiche siano enumerati, come gli errori. È possibile definire le proprie enumerazioni di errore e renderle conformi a ErrorType. – hexdreamer
C'è un detergente (credo) modo per raggiungerlo
extension Notification.Name {
static let onSelectedSkin = Notification.Name("on-selected-skin")
}
e poi si può usare in questo modo
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
Sto usando il codice sopra. Questa è una proprietà statica. –
Questo è un modo fantastico per farlo. Votazione – crashoverride777
Molto pulito, mi piace molto –
Questo è appena 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)
È possibile aggiungere un inizializzatore personalizzato a NSNotification.Name
extension NSNotification.Name {
enum Notifications: String {
case foo, bar
}
init(_ value: Notifications) {
self = NSNotification.Name(value.rawValue)
}
}
Usage:
NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)
modo più semplice:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
NSNotification.Name(rawValue: "myNotificationName")
Ho fatto la mia implementazione mescolando le cose da lì a lì, e trovo questo come il più conveniente. La condivisione per chi qualsiasi che 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)
}
}
Il vantaggio di utilizzare le enumerazioni è che arriviamo al compilatore di verificare che il nome sia corretto. Riduce i potenziali problemi e facilita il refactoring.
Per coloro che amano utilizzando le enumerazioni, invece di stringhe tra virgolette per i nomi di notifica, 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 è possibile utilizzare in questo modo:
NotificationCenter.default.post(.somethingHappened)
Anche se estranei al domanda, lo stesso può essere fatto con storyboard segues, 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)
}
}
Poi, sul controller della vista, come lo chiamano:
perform(segue: .unwindToX)
se si utilizza notifiche personalizzate stringa sola, non c'è motivo di estendere tutte le classi ma String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
Questo è esattamente il modo in cui inizialmente pensavo che avrebbe dovuto funzionare: le notifiche dovrebbero essere enumerate. Grazie per il trucco! – hexdreamer
Nessun problema! Ho modificato il codice per includere la conformazione dell'estensione a 'NotificationName' in modo che la proprietà' name' sia aggiunta alle enumerazioni conformi al protocollo. –
IMO rigorosamente equivalente ma più logico, è possibile definire l'estensione su NotificationName (anziché RawRepresentable) in questo modo: 'estensione NotificationName dove Self: RawRepresentable, Self.RawValue == String {' – jlj