Réponses:
Vous pouvez également utiliser un protocole pour cela
protocol NotificationName {
var name: Notification.Name { get }
}
extension RawRepresentable where RawValue == String, Self: NotificationName {
var name: Notification.Name {
get {
return Notification.Name(self.rawValue)
}
}
}
Et puis définissez vos noms de notification comme enum
vous le souhaitez. Par exemple:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
Et utilisez-le comme
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
De cette façon, les noms des notifications seront découplés de la Fondation Notification.Name
. Et vous n'aurez qu'à modifier votre protocole en cas de mise en œuvre pour des Notification.Name
changements.
NotificationName
que la name
propriété ne soit ajoutée qu'aux énumérations conformes au protocole.
extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
Il existe un moyen plus propre (je pense) d'y parvenir
extension Notification.Name {
static let onSelectedSkin = Notification.Name("on-selected-skin")
}
Et puis tu peux l'utiliser comme ça
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
extension NSNotification.Name
au lieu de extension Notification.Name
. Sinon Swift 3 plaintes avec'Notification' is ambiguous for type lookup in this context
Notification.post est défini comme:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
En Objective-C, le nom de la notification est une NSString simple. Dans Swift, il est défini comme NSNotification.Name.
NSNotification.Name est défini comme:
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
C'est un peu bizarre, car je m'attendrais à ce que ce soit un Enum, et non une structure personnalisée avec apparemment aucun avantage.
Il existe un typealias dans Notification for NSNotification.Name:
public typealias Name = NSNotification.Name
La partie déroutante est que la notification et la NSNotification existent dans Swift
Donc, pour définir votre propre notification personnalisée, faites quelque chose comme:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Alors pour l'appeler:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Notification.Name
s'agissait d'une énumération, personne ne pourrait définir de nouvelles notifications. Nous utilisons des structures pour les types de type autrement enum qui doivent permettre l'ajout de nouveaux membres. (Voir la proposition d'évolution rapide .)
Notification
est un type valeur (une structure), de sorte qu'il puisse bénéficier de la sémantique de Swift pour la (im) mutabilité de la valeur. Généralement, les types Foundation abandonnent leur "NS" dans Swift 3, mais là où l'un des nouveaux types de valeur Foundation existe pour le remplacer, l'ancien type de référence reste (en conservant le nom "NS") afin que vous puissiez toujours l'utiliser lorsque vous avez besoin d'une sémantique de référence ou de la sous-classer. Voir la proposition .
Manière plus simple:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
Vous pouvez ajouter un initialiseur personnalisé à 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)
case
s dans une énumération doivent être en minuscules, pas l'énumération elle-même. Les noms de type sont en majuscules et les énumérations sont des types.
Je peux suggérer une autre option qui est similaire à ce que @CesarVarela a suggéré.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Cela vous permettra de publier et de vous abonner facilement aux notifications.
NotificationCenter.default.post(Notification(name: .notificationName))
J'espère que ceci vous aidera.
J'ai fait ma propre implémentation en mélangeant les choses à partir de là et de là, et je trouve que c'est la plus pratique. Partage pour ceux qui pourraient être intéressés:
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")
C'est juste une référence
// 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)
L'avantage d'utiliser des enums est que nous demandons au compilateur de vérifier que le nom est correct. Réduit les problèmes potentiels et facilite la refactorisation.
Pour ceux qui aiment utiliser des énumérations au lieu de chaînes entre guillemets pour les noms de notification, ce code fait l'affaire:
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)
}
}
Ensuite, vous pouvez l'utiliser comme ceci:
NotificationCenter.default.post(.somethingHappened)
Bien que sans rapport avec la question, la même chose peut être faite avec les séquences de storyboard, pour éviter de taper des chaînes entre guillemets:
enum StoryboardSegue: String {
case toHere
case toThere
case unwindToX
}
extension UIViewController {
func perform(segue: StoryboardSegue) {
performSegue(withIdentifier: segue.rawValue, sender: self)
}
}
Ensuite, sur votre contrôleur de vue, appelez-le comme:
perform(segue: .unwindToX)
si vous utilisez des notifications personnalisées contenant uniquement des chaînes, il n'y a aucune raison d'étendre les classes mais String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
Si vous souhaitez que cela fonctionne correctement dans un projet qui utilise à la fois Objective-C et Swift en même temps, j'ai trouvé qu'il était plus facile de créer les notifications dans Objective-C.
Créez un fichier .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";
Dans votre MyProject-Bridging-Header.h
(nommé d'après votre projet) pour les exposer à Swift.
#import "CustomNotifications.h"
Utilisez vos notifications dans Objective-C comme ceci:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];
Et dans Swift (5) comme ceci:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)