NSNotificationCenter addObserver trong Swift


392

Làm thế nào để bạn thêm một người quan sát trong Swift vào trung tâm thông báo mặc định? Tôi đang cố gắng chuyển dòng mã này để gửi thông báo khi mức pin thay đổi.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];

Bạn đang hỏi cụ thể là gì? Bộ chọn hoạt động như thế nào?
nschum

1
Tôi đã không nhận ra loại "Selector" chỉ là một chuỗi trong Swift. Không đề cập đến nó trong các tài liệu.
Berry Blue

Câu trả lời:


443

Nó giống như API Objective-C, nhưng sử dụng cú pháp của Swift.

Swift 4.2 & Swift 5:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(self.batteryLevelChanged),
    name: UIDevice.batteryLevelDidChangeNotification,
    object: nil)

Nếu người quan sát của bạn không kế thừa từ một đối tượng Objective-C, bạn phải thêm tiền tố vào phương thức của mình @objcđể sử dụng nó làm công cụ chọn.

@objc private func batteryLevelChanged(notification: NSNotification){     
    //do stuff using the userInfo property of the notification object
}

Xem Tham chiếu lớp NSNotificationCenter , tương tác với API khách quan-C


3
Cảm ơn! Tôi không biết làm thế nào để vượt qua tên bộ chọn trong Swift.
Berry Blue

14
@ BlackBerryBlue, giải pháp trên có hiệu quả với bạn không? Tôi tin rằng bạn cần thay đổi "pinLevelChanged" thành "pinLevelChanged:" nếu chức năng của bạn chấp nhận NSNotification làm tham số.
Olshansk

1
@Olshansk Vâng, bạn đã đúng. Bạn cần điều đó. Cảm ơn!
Berry Blue

Tại sao UIDeviceBatteryLevelDidChangeNotificationkhông có trong ngoặc kép? Đó là một kiểu chuỗi.
kmiklas

13
Hãy chắc chắn chú thích lớp hoặc phương thức đích với @objc.
Klaas

757

Swift 4.0 & Xcode 9.0+:

Gửi (Đăng) Thông báo:

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

HOẶC LÀ

NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil, userInfo: ["Renish":"Dadhaniya"])

Nhận (Nhận) Thông báo:

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Trình xử lý phương thức chức năng để nhận Thông báo:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

Gửi (Đăng) Thông báo:

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

Nhận (Nhận) Thông báo:

NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Trình xử lý phương thức để nhận Thông báo:

func methodOfReceivedNotification(notification: Notification) {
  // Take Action on Notification
}

Xóa thông báo:

deinit {
  NotificationCenter.default.removeObserver(self, name: Notification.Name("NotificationIdentifier"), object: nil)
}

Swift 2.3 & Xcode 7:

Gửi (Đăng) Thông báo

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Nhận (Nhận) Thông báo

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:"NotificationIdentifier", object: nil)

Trình xử lý phương thức nhận thông báo

func methodOfReceivedNotification(notification: NSNotification){
  // Take Action on Notification
}


Đối với các phiên bản Xcode lịch sử ...



Gửi (Đăng) Thông báo

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Nhận (Nhận) Thông báo

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOfReceivedNotification:", name:"NotificationIdentifier", object: nil)

Xóa thông báo

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Remove from all notifications being observed

Trình xử lý phương thức nhận thông báo

func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

Chú thích lớp hoặc phương thức đích với @objc

@objc private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

// Or

dynamic private func methodOfReceivedNotification(notification: NSNotification) {
  // Take Action on Notification
}

21
Hãy chắc chắn chú thích lớp hoặc phương thức đích với @objc.
Klaas

1
@goofansu Bạn có chắc không? Tôi nghĩ rằng bạn phải thêm nó khi đó là một lớp Swift thuần túy.
Klaas

10
methodOFReceivedNoticationphải được chú thích với dynamichoặc là thành viên của một lớp con của NSObject.
Klaas

1
Nếu không, tôi nhận được cảnh báo thời gian chạy object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas

2
@Taylor ALLred, Cảm ơn bạn rất nhiều vì đã xem lại câu trả lời của tôi. Tôi thực sự đánh giá cao đề nghị của bạn. Tôi đã thay đổi nó. Xin vui lòng xem lại nó.
Đổi mới Dadhaniya

46

Một cách hay để làm điều này là sử dụng addObserver(forName:object:queue:using:)phương thức chứ không phải addObserver(_:selector:name:object:)phương thức thường được sử dụng từ mã Objective-C. Ưu điểm của biến thể đầu tiên là bạn không phải sử dụng @objcthuộc tính trên phương thức của mình:

    func batteryLevelChanged(notification: Notification) {
        // do something useful with this information
    }

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil,
        using: batteryLevelChanged)

và bạn thậm chí có thể chỉ sử dụng một bao đóng thay vì một phương thức nếu bạn muốn:

    let observer = NotificationCenter.default.addObserver(
        forName: NSNotification.Name.UIDeviceBatteryLevelDidChange,
        object: nil, queue: nil) { _ in print("🔋") }

Bạn có thể sử dụng giá trị được trả về để ngừng nghe thông báo sau:

    NotificationCenter.default.removeObserver(observer)

Đã từng có một lợi thế khác khi sử dụng phương thức này, đó là nó không yêu cầu bạn sử dụng các chuỗi chọn mà trình biên dịch không thể kiểm tra tĩnh và do đó rất dễ bị phá vỡ nếu phương thức được đổi tên, nhưng Swift 2.2 và sau này bao gồm các #selectorbiểu thức khắc phục vấn đề đó.


7
Điều đó thật tuyệt! Để hoàn thiện tôi cũng chỉ muốn xem ví dụ không đăng ký. Nó là khá khác nhau sau đó addObserver(_:selector:name:object:) cách không đăng ký. Bạn phải giữ đối tượng quay lại addObserverForName(_:object:queue:usingBlock:)và chuyển nó tớiremoveObserver:
Lucas Goossen

1
Điều này cần cập nhật để bao gồm đăng ký lại của đối tượng được trả về bởi addObserverForName(_:object:queue:usingBlock:).
Hyperbole

3
Đây là một câu trả lời tốt hơn nhiều so với của Connor hoặc Renish (cả hai ở trên tại thời điểm nhận xét này) bởi vì nó phải sử dụng các phương pháp Obj-C #selector. Kết quả là Swift-y nhiều hơn và chính xác hơn, IMO. Cảm ơn!
patr1ck 16/07/2016

2
Hãy nhớ rằng, nếu bạn sử dụng điều này trong, giả sử, a UIViewControllervà tham chiếu selftrong lần đóng đó, bạn cần sử dụng [weak self]hoặc bạn sẽ có một chu trình tham chiếu và rò rỉ bộ nhớ.
Rob N

40

Swift 3.0 trong Xcode 8

Swift 3.0 đã thay thế nhiều API "gõ chuỗi" bằng struct"loại trình bao bọc", như trường hợp của NotificationCenter. Thông báo bây giờ được xác định bởi một struct Notfication.Namechứ không phải bởi String. Xem hướng dẫn Di chuyển sang Swift 3 .

Sử dụng trước đó :

// Define identifier
let notificationIdentifier: String = "NotificationIdentifier"

// Register to receive notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name: notificationIdentifier, object: nil)

// Post a notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)

Sử dụng Swift 3.0 mới:

// Define identifier
let notificationName = Notification.Name("NotificationIdentifier")

// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification), name: notificationName, object: nil)

// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)

Tất cả các loại thông báo hệ thống hiện được định nghĩa là hằng số tĩnh trên Notification.Name; tức là .UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChangevv

Bạn có thể gia hạn Notification.Namevới thông báo tùy chỉnh của riêng mình để phù hợp với thông báo hệ thống:

// Definition:
extension Notification.Name {
    static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}

// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)

24
  1. Khai báo tên thông báo

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. Bạn có thể thêm người quan sát theo hai cách:

    Sử dụng Selector

    NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: .purchaseDidFinish, object: nil)
    
    @objc func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }

    hoặc sử dụng block

    NotificationCenter.default.addObserver(forName: .purchaseDidFinish, object: nil, queue: nil) { [weak self] (notification) in
        guard let strongSelf = self else {
            return
        }
    
        strongSelf.myFunction(notification: notification)
    }
    
    func myFunction(notification: Notification) {
        print(notification.object ?? "") //myObject
        print(notification.userInfo ?? "") //[AnyHashable("key"): "Value"]
    }
  3. Gửi thông báo của bạn

    NotificationCenter.default.post(name: .purchaseDidFinish, object: "myObject", userInfo: ["key": "Value"])

từ iOS 9 và OS X 10.11. Không còn cần thiết cho người quan sát NSNotificationCenter tự hủy đăng ký khi bị giải phóng. thêm thông tin

Để blockthực hiện dựa trên, bạn cần thực hiện một điệu nhảy yếu-mạnh nếu bạn muốn sử dụng selfbên trong khối. thêm thông tin

Các nhà quan sát dựa trên khối cần được loại bỏ thêm thông tin

let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)

5
"từ iOS 9 và OS X 10.11. Không còn cần thiết cho người quan sát NSNotificationCenter tự hủy đăng ký khi bị xử lý." Điều này chỉ đúng với các nhà quan sát dựa trên Selector. Các nhà quan sát dựa trên khối vẫn cần phải được loại bỏ.
Abhinav

8

Truyền dữ liệu bằng NSNotificationCenter

Bạn cũng có thể truyền dữ liệu bằng NotificationCentre trong swift 3.0 và NSNotificationCenter trong swift 2.0.

Phiên bản Swift 2.0

Truyền thông tin bằng userInfo là từ điển tùy chọn loại [NSObject: AnyObject]?

let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
 NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: imageDataDict)

// Register to receive notification in your class
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: notificationName, object: nil)

// handle notification
func showSpinningWheel(notification: NSNotification) {
  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Phiên bản Swift 3.0

Bây giờ userInfo mất [AnyHashable: Any]? như một đối số, mà chúng tôi cung cấp dưới dạng từ điển trong Swift

let imageDataDict:[String: UIImage] = ["image": image]

// post a notification
 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict) 
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// handle notification
func showSpinningWheel(_ notification: NSNotification) {

  if let image = notification.userInfo?["image"] as? UIImage {
  // do something with your image   
  }
}

Nguồn thông qua dữ liệu sử dụng NotificationCentre (nhanh chóng 3.0) và NSNotificationCenter (nhanh chóng 2.0)


Rất vui khi biết nó đã giúp bạn :)
Sahil

6

Trong Swift 5

Giả sử nếu muốn nhận dữ liệu từ ViewControllB đến ViewControllA

ViewControllA (Bộ thu)

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Code for Passing Data through Notification Observer - - - - -
        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: - - - - - Method for receiving Data through Post Notificaiton - - - - -
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllB (Người gửi)

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: - - - - - Set data for Passing Data Post Notification - - - - -
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }

}

2

Tôi có thể thực hiện một trong những cách sau để sử dụng thành công bộ chọn - mà không cần chú thích bất cứ điều gì với @objc:

NSNotificationCenter.defaultCenter().addObserver(self,
    selector:"batteryLevelChanged:" as Selector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

HOẶC LÀ

let notificationSelector: Selector = "batteryLevelChanged:"

NSNotificationCenter.defaultCenter().addObserver(self,
    selector: notificationSelector,
    name:"UIDeviceBatteryLevelDidChangeNotification",
    object:nil)    

Phiên bản xcrun của tôi hiển thị Swift 1.2 và phiên bản này hoạt động trên Xcode 6.4 và Xcode 7 beta 2 (mà tôi nghĩ sẽ sử dụng Swift 2.0):

$xcrun swift --version

Apple Swift version 1.2 (swiftlang-602.0.53.1 clang-602.0.53)

Bạn không cần chú thích @objcnếu lớp người quan sát của bạn kế thừa từ NSObject.
Antonio Favata

Và bạn không cần phải đúc một cách rõ ràng Stringđể Selectormột trong hai. :)
Antonio Favata

@alfvata: Lớp người quan sát của tôi không kế thừa từ NSObject. Nó kế thừa từ AnyObject, kiểu Swift. Việc truyền chuỗi một cách rõ ràng cho Selector sẽ cho phép tôi tránh thực hiện bất kỳ cách giải quyết nào khác liên quan đến Mục tiêu-C.
leanne

Tôi không chắc tôi hiểu cách thức hoạt động. Tôi đã xóa @objcchú thích khỏi phương thức trong NSObjectlớp không quan sát viên của mình, thêm phép as Selectortruyền vào Stringtên bộ chọn và khi thông báo kích hoạt ứng dụng gặp sự cố. Phiên bản Swift của tôi giống hệt như phiên bản của bạn.
Antonio Favata

3
@alfavata, tôi không biết phải nói gì với bạn. Tôi hiện đang sử dụng Xcode Beta 4 và nó vẫn hoạt động. Dự án của tôi hoàn toàn là Swift; không có thành phần Objective-C. Có lẽ điều đó làm cho một sự khác biệt. Có lẽ có một cái gì đó khác nhau trong các thiết lập dự án. Có bất kỳ số lượng khả năng! Tôi sẽ nói: miễn là @objcchú thích có hiệu quả với bạn, và cách này thì không, sau đó tiếp tục chú thích!
leanne

2

Trong swift 2.2 - XCode 7.3, chúng tôi sử dụng #selectorchoNSNotificationCenter

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(rotate), name: UIDeviceOrientationDidChangeNotification, object: nil)

2

Chúng ta cũng nên loại bỏ thông báo.

Ví dụ.

deinit 
{
  NotificationCenter.default.removeObserver(self, name:NSNotification.Name(rawValue: "notify"), object: nil)

}

2
Tôi tin rằng bạn không cần điều này kể từ iOS 9. Nó được thực hiện tự động.
Viktor Kucera

1

Trong swift 3, Xcode 8.2: - kiểm tra mức trạng thái pin

//Add observer
NotificationCenter.default.addObserver(self, selector: #selector(batteryStateDidChange), name: NSNotification.Name.UIDeviceBatteryStateDidChange, object: nil)


 //Fired when battery level changes

 func batteryStateDidChange(notification: NSNotification){
        //perform manipulation here
    }

1

NSNotificationCenter thêm cú pháp quan sát viên trong Swift 4.0 cho iOS 11

  NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

Đây là loại bàn phím thông báoWillShow. Loại khác có thể được chọn từ tùy chọn có sẵn

Bộ chọn là loại @objc func xử lý cách bàn phím sẽ hiển thị (đây là chức năng người dùng của bạn)


Chỉ cần làm rõ cho bất cứ ai đọc câu trả lời này: "Bộ chọn là loại @objc func ..." có nghĩa là chức năng liên quan đến #selectorphải được chú thích với @objc. Ví dụ: @objc func keyboardShow() { ... }Điều đó đã ném tôi trong một phút trong Swift 4!
leanne

0

Swift 5 & Xcode 10.2:

NotificationCenter.default.addObserver(
            self,
            selector: #selector(batteryLevelDidChangeNotification),
            name: UIDevice.batteryLevelDidChangeNotification,
            object: nil)

0

Người quan sát thông báo Swift 5

override func viewDidLoad() {
    super.viewDidLoad() 
    NotificationCenter.default.addObserver(self, selector: #selector(batteryLevelChanged), name: UIDevice.batteryLevelDidChangeNotification, object: nil)
}

@objc func batteryLevelChanged(notification : NSNotification){
    //do here code
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: UIDevice.batteryLevelDidChangeNotification, 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.