首页 > 解决方案 > 观察者沟通模式

问题描述

我有一个非常简单的实现(3 个类)来获得基础知识。但它有Bug。

事实:它会通知 ViewController,但不会通知 SecondVC 的屏幕。想知道为什么或在哪里!

Git:https ://github.com/marlhex/ObserverPattern

相关课程:

struct MyNotifications {
    static let broadcast = "BROADCAST"
}
import UIKit

let notificationCtr = NotificationCenter.default

class ViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        notificationCtr.addObserver(self, selector: #selector(notifyObservers), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
    
    @objc func notifyObservers() {
        myLabel.text = "I got Notified"
    }
    
    @IBAction func doBroadcast(_ sender: Any) {
        notificationCtr.post(name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: self)
    }

}
import UIKit

class SecondVC: UIViewController {
    
    @IBOutlet weak var mySecondLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        notificationCtr.addObserver(self, selector: #selector(notifyObserverstoo), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
   
    @objc func notifyObserverstoo() {
        mySecondLabel.text = "I got Notified too" //Bug only notifies one view controller
    }
}

标签: swiftobserver-patternobservers

解决方案


您正在使用 UITabBarController 来托管视图。系统最初只加载它需要显示的视图控制器(ViewController在这种情况下)。然后,一旦您单击 的选项卡SecondVC,它就会加载该视图控制器。

您可以通过在of 中添加print声明来验证这一点。viewDidLoadSecondVC

您还可以验证,如果您在返回并按下SecondVC之前导航到,则在这种情况下,两个视图控制器都会收到通知。ViewControllerNotify

所以,这不是一个错误——它只是加载视图时的一个实现细节。

如果您想找到一种方法来确保SecondVC在加载时可以访问该信息,您有两种选择:

  1. 依靠不同的传播状态的系统
  2. 将您的通知侦听器放入required init?(coder: NSCoder)而不是viewDidLoad(在设置期间确实会调用它)。但这有一个警告UILabel还不会加载,因此您必须存储该状态以供以后加载。mySecondLabel之前尝试访问viewDidLoad会导致崩溃。

如果您要使用该init方法,则更新存储通知的更新代码:

class SecondVC: UIViewController {
    
    // MARK: - Properties
    @IBOutlet weak var mySecondLabel: UILabel?
    
    var haveBeenNotified = false //to store whether the notification has been seen
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        notificationCtr.addObserver(self, selector: #selector(notifyObserverstoo), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
    
    // MARK: - Life Cycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        
        print("Loaded second view controller")
        
        if haveBeenNotified {
            mySecondLabel?.text = "Was notified before viewDidLoad"
        }
    }
    
    // MARK: - Observer Selector Functions
    @objc func notifyObserverstoo() {
        haveBeenNotified = true
        mySecondLabel?.text = "I got Notified too"
    }

}

推荐阅读