NSNotificationCenter addObserver ใน Swift


392

คุณจะเพิ่มผู้สังเกตการณ์ใน Swift ไปยังศูนย์การแจ้งเตือนเริ่มต้นได้อย่างไร ฉันพยายามพอร์ตของรหัสนี้ที่ส่งการแจ้งเตือนเมื่อระดับแบตเตอรี่เปลี่ยน

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

คุณถามอะไรโดยเฉพาะ ตัวเลือกทำงานอย่างไร
nschum

1
ฉันไม่ทราบว่าประเภท "Selector" เป็นเพียงสตริงใน Swift ไม่พูดถึงมันในเอกสาร
Berry Blue

คำตอบ:


442

มันเหมือนกับ Objective-C API แต่ใช้ไวยากรณ์ของ Swift

Swift 4.2 และ Swift 5:

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

หากผู้สังเกตการณ์ของคุณไม่สืบทอดจากวัตถุ Objective-C คุณจะต้องนำหน้าวิธีการของคุณด้วย@objcเพื่อที่จะใช้มันเป็นตัวเลือก

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

ดูNSNotificationCenter ชั้นอ้างอิง , การโต้ตอบกับ APIs Objective-C


3
ขอบคุณ! ฉันไม่รู้วิธีส่งชื่อตัวเลือกใน Swift
Berry Blue

14
@ BerryBlue โซลูชันดังกล่าวทำงานให้คุณหรือไม่ ฉันเชื่อว่าคุณต้องเปลี่ยน "batteryLevelChanged" เป็น "batteryLevelChanged:" หากฟังก์ชั่นของคุณยอมรับ NSNotification เป็นพารามิเตอร์
Olshansk

1
@Olshansk ใช่คุณถูกต้อง คุณต้องการสิ่งนั้น ขอบคุณ!
แบล็กเบอร์สีฟ้า

ทำไมUIDeviceBatteryLevelDidChangeNotificationไม่อยู่ในเครื่องหมายคำพูด? มันเป็นประเภทสตริง
kmiklas

13
@objcให้แน่ใจว่าใส่คำอธิบายประกอบทั้งชั้นเรียนหรือวิธีการเป้าหมายด้วย
Klaas

757

Swift 4.0 และ Xcode 9.0+:

ส่ง (โพสต์) การแจ้งเตือน:

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

หรือ

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

รับ (รับ) การแจ้งเตือน:

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

ฟังก์ชั่น - ตัวจัดการวิธีการสำหรับการแจ้งเตือนที่ได้รับ:

@objc func methodOfReceivedNotification(notification: Notification) {}

Swift 3.0 & Xcode 8.0+:

ส่ง (โพสต์) การแจ้งเตือน:

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

รับ (รับ) การแจ้งเตือน:

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

ตัวจัดการวิธีสำหรับการแจ้งเตือนที่ได้รับ:

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

ลบการแจ้งเตือน:

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

Swift 2.3 & Xcode 7:

ส่ง (โพสต์) การแจ้งเตือน

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

รับ (รับ) การแจ้งเตือน

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

ตัวจัดการวิธีสำหรับการแจ้งเตือนที่ได้รับ

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


สำหรับเวอร์ชั่น Xcode ประวัติศาสตร์ ...



ส่ง (โพสต์) การแจ้งเตือน

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

รับ (รับ) การแจ้งเตือน

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

แจ้งลบ

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

ตัวจัดการวิธีสำหรับการแจ้งเตือนที่ได้รับ

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

ใส่คำอธิบายประกอบคลาสหรือวิธีการเป้าหมายด้วย @objc

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

// Or

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

21
@objcให้แน่ใจว่าใส่คำอธิบายประกอบทั้งชั้นเรียนหรือวิธีการเป้าหมายด้วย
Klaas

1
@goofansu คุณแน่ใจหรือไม่ ฉันคิดว่าคุณต้องเพิ่มมันเมื่อมันเป็นคลาส Swift ล้วนๆ
Klaas

10
methodOFReceivedNoticationจะต้องมีคำอธิบายประกอบด้วยdynamicหรือเป็นสมาชิกของคลาสย่อยของ NSObject
Klaas

1
ถ้าไม่ได้ผมได้รับการเตือนรันไทม์object 0x7fd68852d710 of class 'TestNotifications.MyObject' does not implement methodSignatureForSelector: -- trouble ahead,Unrecognized selector -[TestNotifications.MyObject methodOFReceivedNotication:]
Klaas

2
@TaylorAllred ขอบคุณมากสำหรับคำตอบของฉัน ฉันขอขอบคุณข้อเสนอแนะของคุณ ฉันเปลี่ยนมันแล้ว กรุณาตรวจสอบมัน
Renish Dadhaniya

46

วิธีที่ดีในการทำเช่นนี้คือการใช้addObserver(forName:object:queue:using:)วิธีการแทนaddObserver(_:selector:name:object:)วิธีที่มักใช้จากรหัส Objective-C ข้อดีของตัวแปรแรกคือคุณไม่ต้องใช้@objcแอตทริบิวต์กับวิธีการของคุณ:

    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)

และคุณสามารถใช้การปิดแทนวิธีการได้ถ้าคุณต้องการ:

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

คุณสามารถใช้ค่าที่ส่งคืนเพื่อหยุดฟังการแจ้งเตือนในภายหลัง:

    NotificationCenter.default.removeObserver(observer)

เคยมีข้อได้เปรียบอีกอย่างหนึ่งในการใช้วิธีนี้ซึ่งก็คือมันไม่ต้องการให้คุณใช้สตริงตัวเลือกที่คอมไพเลอร์ไม่สามารถตรวจสอบแบบคงที่ได้ ต่อมารวมถึง#selectorนิพจน์ที่แก้ไขปัญหานั้น


7
มันเยี่ยมมาก! เพื่อความสมบูรณ์ฉันต้องการเห็นตัวอย่างที่ไม่ลงทะเบียนเช่นกัน มันค่อนข้างแตกต่างจากaddObserver(_:selector:name:object:) วิธีการลงทะเบียน คุณต้องเก็บสิ่งของที่ถูกส่งคืนaddObserverForName(_:object:queue:usingBlock:)และส่งผ่านไปยังremoveObserver:
Lucas Goossen

1
ความต้องการนี้จะรวมการปรับปรุง addObserverForName(_:object:queue:usingBlock:)de-ลงทะเบียนของค้านกลับโดย
อธิที่

3
นี่เป็นคำตอบที่ดีกว่าคอนเนอร์หรือ Renish's (ทั้งสองข้างต้นในขณะที่ความคิดเห็นนี้) เพราะมันต้องใช้วิธี Obj-C #selector ผลที่ได้คือ Swift-y และ IMO ที่ถูกต้องมากขึ้น ขอบคุณ!
patr1ck

2
จำไว้ว่าถ้าคุณใช้สิ่งนี้ในการพูด a UIViewControllerและอ้างถึงselfในการปิดนั้นคุณจำเป็นต้องใช้[weak self]หรือคุณจะมีรอบการอ้างอิงและหน่วยความจำรั่ว
Rob N

40

Swift 3.0 ใน Xcode 8

Swift 3.0 ได้แทนที่ APIs ที่ "พิมพ์อย่างเข้มงวด" จำนวนมากด้วยstruct"wrapper types" ซึ่งเป็นกรณีของ NotificationCenter การแจ้งเตือนจะมีการระบุในขณะนี้โดยมากกว่าโดยstruct Notfication.Name Stringดูการโยกย้ายไปยังสวิฟท์ 3 คู่มือ

การใช้งานก่อนหน้า :

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

ใหม่การใช้ Swift 3.0:

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

ทุกประเภทการแจ้งเตือนระบบจะมีการกำหนดในขณะนี้เป็นค่าคงที่คงที่Notification.Name; เช่น.UIDeviceBatteryLevelDidChange, .UIApplicationDidFinishLaunching, .UITextFieldTextDidChangeฯลฯ

คุณสามารถขยายNotification.Nameด้วยการแจ้งเตือนที่กำหนดเองของคุณเองเพื่อให้สอดคล้องกับการแจ้งเตือนของระบบ:

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

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

24
  1. ประกาศชื่อการแจ้งเตือน

    extension Notification.Name {
        static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
    }
  2. คุณสามารถเพิ่มผู้สังเกตการณ์ได้สองวิธี:

    การใช้ 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"]
    }

    หรือใช้ 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. โพสต์การแจ้งเตือนของคุณ

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

จาก iOS 9 และ OS X 10.11 มันไม่จำเป็นสำหรับผู้สังเกตการณ์ NSNotificationCenter อีกต่อไปที่จะยกเลิกการลงทะเบียนตัวเองเมื่อถูกจัดสรรคืน ข้อมูลเพิ่มเติม

สำหรับการblockติดตั้งแบบพื้นฐานคุณจะต้องทำการเต้นที่อ่อนแอมากหากคุณต้องการใช้selfภายในบล็อก ข้อมูลเพิ่มเติม

ผู้สังเกตการณ์ที่ถูกบล็อกจะต้องลบข้อมูลเพิ่มเติม

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

5
"จาก iOS 9 และ OS X 10.11 ไม่จำเป็นสำหรับผู้สังเกตการณ์ NSNotificationCenter อีกต่อไปที่จะยกเลิกการลงทะเบียนตัวเองเมื่อถูกจัดสรรคืน" สิ่งนี้เป็นจริงสำหรับผู้สังเกตการณ์ที่ใช้ตัวเลือกเท่านั้น ผู้สังเกตการณ์ที่ถูกบล็อกยังคงต้องถูกลบออก
Abhinav

8

ส่งผ่านข้อมูลโดยใช้ NSNotificationCenter

คุณสามารถส่งผ่านข้อมูลโดยใช้ NotificationCentre ใน swift 3.0 และ NSNotificationCenter ใน swift 2.0

เวอร์ชั่น Swift 2.0

ส่งผ่านข้อมูลโดยใช้ userInfo ซึ่งเป็นพจนานุกรมตัวเลือกประเภท [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   
  }
}

เวอร์ชั่นของ Swift 3.0

userInfo ใช้ [AnyHashable: Any] แล้วหรือยัง? เป็นอาร์กิวเมนต์ซึ่งเราให้เป็นพจนานุกรมตามตัวอักษรใน 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   
  }
}

ข้อมูล Passที่มาโดยใช้ NotificationCentre (swift 3.0) และ NSNotificationCenter (swift 2.0)


ดีใจที่ได้ยินมันช่วยให้คุณ :)
Sahil

6

ในSwift 5

สมมติว่าต้องการรับข้อมูลจาก ViewControllerB ไปยัง ViewControllerA

ViewControllerA (ตัวรับสัญญาณ)

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

ViewControllerB (ผู้ส่ง)

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

ฉันสามารถเลือกทำอย่างใดอย่างหนึ่งต่อไปนี้เพื่อใช้ตัวเลือก - โดยไม่ต้องใส่หมายเหตุใด ๆ ด้วย @objc:

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

หรือ

let notificationSelector: Selector = "batteryLevelChanged:"

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

เวอร์ชัน xcrun ของฉันแสดง Swift 1.2 และทำงานบน Xcode 6.4 และ Xcode 7 beta 2 (ซึ่งฉันคิดว่าจะใช้ Swift 2.0):

$xcrun swift --version

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

คุณไม่จำเป็นต้องใส่คำอธิบายประกอบกับถ้าสืบทอดระดับสังเกตการณ์ของคุณจาก@objc NSObject
Antonio Favata

และคุณไม่จำเป็นต้องทำการ a StringถึงSelectorอย่างชัดเจน :)
Antonio Favata

@alfvata: ชั้นผู้สังเกตการณ์ของฉันไม่สืบทอดจาก NSObject มันสืบทอดมาจาก AnyObject สไตล์สวิฟท์ การคัดเลือกสตริงให้กับ Selector อย่างชัดเจนทำให้ฉันสามารถหลีกเลี่ยงการแก้ไขปัญหาอื่น ๆ ที่เกี่ยวกับ Objective-C
ลีแอนน์

ฉันไม่แน่ใจว่าฉันเข้าใจวิธีการทำงาน ฉันลบ@objcคำอธิบายประกอบออกจากวิธีการในNSObjectคลาสที่ไม่ใช่ผู้สังเกตการณ์ของฉันเพิ่มการas Selectorส่งไปยังStringชื่อตัวเลือกและเมื่อการแจ้งเตือน fires แอปขัดข้อง เวอร์ชั่น Swift ของฉันเหมือนกับของคุณทุกประการ
Antonio Favata

3
@alfavata ฉันไม่รู้จะบอกอะไรคุณ ตอนนี้ฉันใช้ Xcode Beta 4 และมันยังทำงานอยู่ โครงการของฉันเร็วมาก ไม่มีส่วนประกอบ Objective-C บางทีนั่นอาจสร้างความแตกต่าง อาจมีบางสิ่งที่แตกต่างในการตั้งค่าโครงการ มีความเป็นไปได้ไม่มาก! ฉันจะบอกว่า: ตราบใดที่การเพิ่มความคิดเห็น@objcทำงานได้ดีสำหรับคุณและวิธีนี้ใช้ไม่ได้แล้วให้คำอธิบายประกอบต่อไป!
ลีแอนน์

2

ใน swift 2.2 - XCode 7.3 เราใช้#selectorเพื่อNSNotificationCenter

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

2

เราควรลบการแจ้งเตือนด้วย

อดีต

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

}

2
ฉันเชื่อว่าคุณไม่ต้องการสิ่งนี้ตั้งแต่ iOS 9 มันทำโดยอัตโนมัติ
Viktor Kucera

1

ใน swift 3, Xcode 8.2: - การตรวจสอบระดับแบตเตอรี่

//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เพิ่มไวยากรณ์ผู้สังเกตการณ์ในSwift 4.0สำหรับiOS 11

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

นี่คือสำหรับชื่อประเภทการแจ้งเตือน keyboardWillShow สามารถเลือกประเภทอื่นจากตัวเลือกที่มี

Selector เป็นประเภท @objc func ซึ่งจัดการวิธีที่คีย์บอร์ดจะแสดง (นี่คือฟังก์ชั่นผู้ใช้ของคุณ)


เพียงชี้แจงสำหรับทุกคนที่อ่านคำตอบนี้: "ตัวเลือกเป็นประเภท @objc func ..." หมายถึงฟังก์ชั่นที่เกี่ยวข้องกับการจะต้องกำกับด้วย#selector @objcตัวอย่างเช่น@objc func keyboardShow() { ... }นั่นทำให้ฉันเสียเวลาหนึ่งนาทีใน Swift 4!
ลีแอนน์

0

Swift 5 & Xcode 10.2:

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

0

ผู้สังเกตการณ์การแจ้งเตือน 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)

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.