W Objective-C niestandardowe powiadomienie jest zwykłym NSStringiem, ale w wersji WWDC Swift 3 nie jest oczywiste, jakie powinno być.
W Objective-C niestandardowe powiadomienie jest zwykłym NSStringiem, ale w wersji WWDC Swift 3 nie jest oczywiste, jakie powinno być.
Odpowiedzi:
Możesz również użyć do tego protokołu
protocol NotificationName {
var name: Notification.Name { get }
}
extension RawRepresentable where RawValue == String, Self: NotificationName {
var name: Notification.Name {
get {
return Notification.Name(self.rawValue)
}
}
}
A następnie zdefiniuj nazwy powiadomień w enum
dowolnym miejscu. Na przykład:
class MyClass {
enum Notifications: String, NotificationName {
case myNotification
}
}
I używaj go jak
NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)
W ten sposób nazwy powiadomień zostaną oddzielone od Fundacji Notification.Name
. I będziesz musiał zmodyfikować swój protokół tylko w przypadku implementacji Notification.Name
zmian.
NotificationName
tak name
nieruchomość jest dodawany jedynie teksty stałe, które są zgodne z protokołem.
extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
Jest na to czystszy (myślę) sposób
extension Notification.Name {
static let onSelectedSkin = Notification.Name("on-selected-skin")
}
A potem możesz go używać w ten sposób
NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
extension NSNotification.Name
zamiast extension Notification.Name
. W przeciwnym razie Swift 3 'Notification' is ambiguous for type lookup in this context
Notification.post definiuje się jako:
public func post(name aName: NSNotification.Name, object anObject: AnyObject?)
W Objective-C nazwą powiadomienia jest zwykły NSString. W Swift jest zdefiniowany jako NSNotification.Name.
NSNotification.Name jest zdefiniowane jako:
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
To trochę dziwne, ponieważ spodziewałbym się, że będzie to Enum, a nie jakaś niestandardowa struktura, która pozornie nie ma więcej korzyści.
W Notification for NSNotification.Name występuje alias typu:
public typealias Name = NSNotification.Name
Mylące jest to, że zarówno Notification, jak i NSNotification istnieją w Swift
Aby zdefiniować własne niestandardowe powiadomienie, zrób coś takiego:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
Następnie nazwij to:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Notification.Name
było wyliczeniem, nikt nie byłby w stanie zdefiniować nowych powiadomień. Używamy struktur dla innych typów wyliczeniowych, które muszą umożliwiać dodawanie nowych członków. (Zobacz propozycję szybkiej ewolucji .)
Notification
jest typem wartości (strukturą), dzięki czemu może skorzystać z semantyki języka Swift dla zmienności wartości (nie). Generalnie typy Foundation porzucają swój „NS” w Swift 3, ale tam, gdzie istnieje jeden z nowych Foundation Value Types, który go zastępuje, stary typ referencyjny pozostaje (zachowując nazwę „NS”), dzięki czemu można go nadal używać, gdy potrzebujesz semantyki referencji lub podklasy jej. Zobacz propozycję .
Łatwiejszy sposób:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
Możesz dodać niestandardowy inicjator do NSNotification.Name
extension NSNotification.Name {
enum Notifications: String {
case foo, bar
}
init(_ value: Notifications) {
self = NSNotification.Name(value.rawValue)
}
}
Stosowanie:
NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)
case
s w wyliczeniu powinny być pisane małymi literami, a nie samo wyliczenie. Nazwy typów są pisane wielkimi literami, a wyliczenia są typami.
Mogę zasugerować inną opcję, podobną do tej, którą zasugerował @CesarVarela.
extension Notification.Name {
static var notificationName: Notification.Name {
return .init("notificationName")
}
}
Umożliwi to łatwe publikowanie i subskrybowanie powiadomień.
NotificationCenter.default.post(Notification(name: .notificationName))
Mam nadzieję, że to ci pomoże.
Zrobiłem własną implementację, mieszając rzeczy stamtąd i tam i uważam to za najwygodniejsze. Udostępnianie dla kogo może być zainteresowany:
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")
To tylko odniesienie
// 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)
Zaletą używania wyliczeń jest to, że otrzymujemy kompilator, aby sprawdzić, czy nazwa jest poprawna. Zmniejsza potencjalne problemy i ułatwia refaktoryzację.
Dla tych, którzy lubią używać wyliczeń zamiast cytowanych ciągów znaków w nazwach powiadomień, ten kod załatwia sprawę:
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)
}
}
Następnie możesz go użyć w ten sposób:
NotificationCenter.default.post(.somethingHappened)
Chociaż nie ma to związku z pytaniem, to samo można zrobić z fragmentami scenorysu, aby uniknąć wpisywania cytowanych ciągów:
enum StoryboardSegue: String {
case toHere
case toThere
case unwindToX
}
extension UIViewController {
func perform(segue: StoryboardSegue) {
performSegue(withIdentifier: segue.rawValue, sender: self)
}
}
Następnie na kontrolerze widoku nazwij to tak:
perform(segue: .unwindToX)
jeśli używasz powiadomień niestandardowych zawierających tylko ciągi znaków, nie ma powodu, aby rozszerzać jakiekolwiek klasy, ale String
extension String {
var notificationName : Notification.Name{
return Notification.Name.init(self)
}
}
Jeśli chcesz, aby to działało czysto w projekcie, który używa jednocześnie Objective-C i Swift, stwierdziłem, że łatwiej jest tworzyć powiadomienia w Objective-C.
Utwórz plik .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";
W swoim MyProject-Bridging-Header.h
(nazwanym na cześć twojego projektu), aby pokazać je Swift.
#import "CustomNotifications.h"
Korzystaj z powiadomień w Objective-C w ten sposób:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];
A w Swift (5) w ten sposób:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)