Làm cách nào để bạn tạo thông báo tùy chỉnh trong Swift 3?


Câu trả lời:


32

Bạn cũng có thể sử dụng một giao thức cho việc này

protocol NotificationName {
    var name: Notification.Name { get }
}

extension RawRepresentable where RawValue == String, Self: NotificationName {
    var name: Notification.Name {
        get {
            return Notification.Name(self.rawValue)
        }
    }
}

Và sau đó xác định tên thông báo của bạn ở enumbất kỳ đâu bạn muốn. Ví dụ:

class MyClass {
    enum Notifications: String, NotificationName {
        case myNotification
    }
}

Và sử dụng nó như

NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)

Bằng cách này, tên thông báo sẽ được tách khỏi Tổ chức Notification.Name. Và bạn sẽ chỉ phải sửa đổi giao thức của mình trong trường hợp triển khai Notification.Namethay đổi.


Đây chính xác là cách mà tôi nghĩ ban đầu nó sẽ hoạt động - thông báo phải là enums. Cảm ơn vì thủ thuật!
hexdreamer

Không vấn đề gì! Tôi đã chỉnh sửa mã để bao gồm cấu trúc của phần mở rộng để NotificationNamethuộc nametính chỉ được thêm vào các enums tuân theo giao thức.
halil_g 24/02/17

IMO tương đương nhưng hợp lý hơn, bạn có thể xác định tiện ích mở rộng trên NotificationName (thay vì RawRepresentable) như sau:extension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj Ngày

387

Có một cách (tôi nghĩ) sạch hơn để đạt được nó

extension Notification.Name {

    static let onSelectedSkin = Notification.Name("on-selected-skin")
}

Và sau đó bạn có thể sử dụng nó như thế này

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)

2
Tôi đang sử dụng mã ở trên. Đây là một thuộc tính tĩnh.
Cesar Varela

3
Rất sạch sẽ, tôi như nó rất nhiều
Tom Wolters

10
extension NSNotification.Name thay vì extension Notification.Name . Nếu không Swift 3 khiếu nại với'Notification' is ambiguous for type lookup in this context
lluisgh

9
Bạn nhận được phiếu bầu tán thành của tôi để thực hiện một lỗi đánh máy trong chuỗi và do đó chứng minh giá trị của tên thông báo gõ: P
Dorian Roy

10
Có thể đáng chú ý rằng đây là phương pháp được Apple đề xuất trong WWDC 2016 Phiên 207 developer.apple.com/videos/play/wwdc2016/207
Leon

36

Notification.post được định nghĩa là:

public func post(name aName: NSNotification.Name, object anObject: AnyObject?)

Trong Objective-C, tên thông báo là một NSString đơn giản. Trong Swift, nó được định nghĩa là NSNotification.Name.

NSNotification.Name được định nghĩa là:

public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
    public init(_ rawValue: String)
    public init(rawValue: String)
}

Điều này hơi kỳ lạ, vì tôi mong đợi nó là một Enum chứ không phải một cấu trúc tùy chỉnh nào đó dường như không còn lợi ích nữa.

Có một kiểu chữ trong Thông báo cho NSNotification.Name:

public typealias Name = NSNotification.Name

Phần khó hiểu là cả Notification và NSNotification đều tồn tại trong Swift

Vì vậy, để xác định thông báo tùy chỉnh của riêng bạn, hãy thực hiện một số cách như:

public class MyClass {
    static let myNotification = Notification.Name("myNotification")
}

Sau đó, để gọi nó:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)

3
Câu trả lời tốt. Một số bình luận: Điều này hơi kỳ lạ, vì tôi nghĩ nó là một Enum - Một enum là một tập hợp đóng . Nếu Notification.Namelà một enum, không ai có thể xác định các thông báo mới. Chúng tôi sử dụng cấu trúc cho các kiểu khác giống như enum cần cho phép thêm thành viên mới. (Xem đề xuất phát triển nhanh chóng .)
rickster

2
Phần khó hiểu là cả Notification và NSNotification đều tồn tại trong Swift - Notificationlà một kiểu giá trị (một cấu trúc), vì vậy nó có thể được hưởng lợi từ ngữ nghĩa của Swift cho khả năng thay đổi giá trị (im). Nói chung, các loại Nền tảng đang loại bỏ "NS" của chúng trong Swift 3, nhưng khi một trong các Loại Giá trị Nền tảng mới tồn tại để thay thế nó, thì loại tham chiếu cũ vẫn tồn tại (giữ tên "NS") để bạn vẫn có thể sử dụng nó khi bạn cần ngữ nghĩa tham chiếu hoặc phân lớp nó. Xem đề xuất .
rickster

Hãy để tôi làm rõ: Tôi mong đợi tên thông báo là enums, giống như Errors. Bạn có thể xác định các enums Lỗi của riêng mình và làm cho chúng phù hợp với ErrorType.
hexdreamer

1
Đúng - Apple ít nhất có thể về mặt lý thuyết đã làm cho NotoficationName (hoặc một số tương tự) trở thành một giao thức mà bạn tạo ra các kiểu phù hợp. Tôi không biết, nhưng có thể có một lý do nào đó mà họ không ... Có lẽ là điều gì đó liên quan đến cầu nối ObjC? Gửi lỗi (đối với mã nguồn mở , Foundation Swift đang ở chế độ mở) nếu bạn có giải pháp tốt hơn.
rickster

2
Bạn có thể đúng ở chỗ nó phải bắt đầu bằng chữ thường.
hexdreamer

13

Cách dễ dàng hơn:

let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)

11

Bạn có thể thêm trình khởi tạo tùy chỉnh vào NSNotification.Name

extension NSNotification.Name {
    enum Notifications: String {
        case foo, bar
    }
    init(_ value: Notifications) {
        self = NSNotification.Name(value.rawValue)
    }
}

Sử dụng:

NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)

1
Thấp hơn trường hợp 'kiểu enum' và 'init (_ loại: loại)' cho Swift 3.0.2
Jalakoo

@Jalakoo Chỉ các cases trong enum mới được viết thường, không phải là enum. Tên loại được viết hoa và enum là loại.
manmal

9

Tôi có thể đề xuất một tùy chọn khác tương tự như những gì @CesarVarela đã đề xuất.

extension Notification.Name {
    static var notificationName: Notification.Name {
        return .init("notificationName")
    }
}

Điều này sẽ cho phép bạn đăng và đăng ký thông báo một cách dễ dàng.

NotificationCenter.default.post(Notification(name: .notificationName))

Hy vọng điều này sẽ giúp bạn.


4

Tôi đã thực hiện việc thực hiện của riêng mình trộn mọi thứ từ đó và ở đó, và thấy điều này là thuận tiện nhất. Chia sẻ cho những ai có thể quan tâm:

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)
    }
}


2

Đây chỉ là tham khảo

// 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)

1

Ưu điểm của việc sử dụng enums là chúng tôi nhận được trình biên dịch để kiểm tra xem tên có đúng không. Giảm các vấn đề tiềm ẩn và giúp tái cấu trúc dễ dàng hơn.

Đối với những người thích sử dụng enums thay vì các chuỗi được trích dẫn cho tên thông báo, mã này thực hiện thủ thuật:

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)
    }
}

Sau đó, bạn có thể sử dụng nó như thế này:

NotificationCenter.default.post(.somethingHappened)

Mặc dù không liên quan đến câu hỏi, điều này cũng có thể được thực hiện với các segues bảng phân cảnh, để tránh nhập các chuỗi được trích dẫn:

enum StoryboardSegue: String {
    case toHere
    case toThere
    case unwindToX
}

extension UIViewController {
    func perform(segue: StoryboardSegue) {
        performSegue(withIdentifier: segue.rawValue, sender: self)
    }
}

Sau đó, trên bộ điều khiển chế độ xem của bạn, hãy gọi nó như sau:

perform(segue: .unwindToX)

> NotificationCenter.default.post(.somethingHappened)Điều này tạo ra một lỗi; các phương thức bạn đã thêm trong tiện ích của mình chấp nhận nhiều đối số hơn.

0

nếu bạn sử dụng thông báo tùy chỉnh chỉ chuỗi, không có lý do gì để mở rộng bất kỳ lớp nào nhưng String

    extension String {
        var notificationName : Notification.Name{
            return Notification.Name.init(self)
        }
    }

0

Câu trả lời của @ CesarVarela là tốt, nhưng để làm cho mã sạch hơn một chút, bạn có thể làm như sau:

extension Notification.Name {
    typealias Name = Notification.Name

    static let onSelectedSkin = Name("on-selected-skin")
    static let onFoo = Name("on-foo")
}

0

Nếu bạn muốn điều này hoạt động rõ ràng trong một dự án sử dụng cả Objective-C và Swift cùng một lúc, tôi thấy việc tạo thông báo trong Objective-C sẽ dễ dàng hơn.

Tạo tệp .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";

Trong của bạn MyProject-Bridging-Header.h(được đặt tên theo dự án của bạn) để cho chúng tiếp xúc với Swift.

#import "CustomNotifications.h"

Sử dụng các thông báo của bạn trong Objective-C như sau:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];

Và trong Swift (5) như thế này:

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.