ใน Objective-C การแจ้งเตือนที่กำหนดเองเป็นเพียง NSString ธรรมดา แต่ไม่ชัดเจนใน Swift 3 เวอร์ชัน WWDC ว่าควรจะเป็นอย่างไร
ใน Objective-C การแจ้งเตือนที่กำหนดเองเป็นเพียง NSString ธรรมดา แต่ไม่ชัดเจนใน Swift 3 เวอร์ชัน WWDC ว่าควรจะเป็นอย่างไร
คำตอบ:
คุณยังสามารถใช้โปรโตคอลสำหรับสิ่งนี้
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
คุณสมบัติให้กับ enums ที่สอดคล้องกับโปรโตคอลเท่านั้น
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 ไม่ใช่โครงสร้างแบบกำหนดเองบางอย่างที่ดูเหมือนจะไม่มีประโยชน์อีกต่อไป
มี typealias ในการแจ้งเตือนสำหรับ NSNotification.Name:
public typealias Name = NSNotification.Name
ส่วนที่ทำให้สับสนคือทั้ง Notification และ NSNotification มีอยู่ใน Swift
ดังนั้นในการกำหนดการแจ้งเตือนที่คุณกำหนดเองให้ทำบางอย่างเช่น:
public class MyClass {
static let myNotification = Notification.Name("myNotification")
}
จากนั้นจะเรียกมันว่า:
NotificationCenter.default().post(name: MyClass.myNotification, object: self)
Notification.Name
เป็น enum จะไม่มีใครกำหนดการแจ้งเตือนใหม่ได้ เราใช้โครงสร้างสำหรับประเภทที่คล้ายกับ enum ที่ต้องอนุญาตให้เพิ่มสมาชิกใหม่ (ดูข้อเสนอวิวัฒนาการที่รวดเร็ว )
Notification
เป็นประเภทค่า (โครงสร้าง) เพื่อให้ได้รับประโยชน์จากความหมายของ Swift สำหรับความไม่แน่นอนของค่า (im) โดยทั่วไปประเภทของมูลนิธิจะทิ้ง "NS" ใน Swift 3 แต่ในกรณีที่ประเภทมูลค่าพื้นฐานใหม่มีอยู่เพื่อแทนที่ประเภทการอ้างอิงแบบเก่าจะเกาะติดรอบ ๆ (ยังคงชื่อ "NS" ไว้) เพื่อให้คุณยังคงสามารถใช้งานได้เมื่อ คุณต้องการความหมายอ้างอิงหรือคลาสย่อย ดูข้อเสนอ
วิธีที่ง่ายกว่า:
let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
คุณสามารถเพิ่ม initializer แบบกำหนดเองใน 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 เอง ชื่อประเภทเป็นตัวพิมพ์ใหญ่และ enums เป็นประเภท
ฉันอาจแนะนำตัวเลือกอื่นที่คล้ายกับที่ @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)
}
}
NSNotification.Name(rawValue: "myNotificationName")
นี่เป็นเพียงข้อมูลอ้างอิง
// 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 คือเราจะได้คอมไพเลอร์ตรวจสอบว่าชื่อนั้นถูกต้อง ลดปัญหาที่อาจเกิดขึ้นและทำให้การปรับโครงสร้างใหม่ง่ายขึ้น
สำหรับผู้ที่ชอบใช้ 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)
แม้ว่าจะไม่เกี่ยวข้องกับคำถาม แต่ก็สามารถทำได้เช่นเดียวกันกับ storyboard segues เพื่อหลีกเลี่ยงการพิมพ์สตริงที่ยกมา:
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)
}
}
คำตอบของ @ CesarVarela นั้นดี แต่เพื่อให้โค้ดสะอาดขึ้นเล็กน้อยคุณสามารถทำสิ่งต่อไปนี้:
extension Notification.Name {
typealias Name = Notification.Name
static let onSelectedSkin = Name("on-selected-skin")
static let onFoo = Name("on-foo")
}
หากคุณต้องการให้สิ่งนี้ทำงานได้อย่างสมบูรณ์ในโครงการที่ใช้ทั้ง 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];
และใน Swift (5) เช่นนี้:
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)