iOS Swift:UITabBarController内视图控制器双向传值首次点击失效问题
问题分析与修复方案
你猜的完全没错!核心问题就是观察者的注册时机晚于通知的发送时机,导致首次点击Profile标签时,通知已经发出去了,但Profile控制器还没来得及注册观察者,自然收不到事件。
具体流程拆解
首次点击Profile标签时的执行顺序是:
tabBarController(_:shouldSelect:)触发,Home控制器发送通知- Profile控制器被初始化,执行
viewDidLoad(如果是第一次创建) - Profile控制器的视图显示,执行
viewDidAppear
你之前把观察者注册放在viewDidAppear里,这时候通知已经在第一步发送完了,所以changeCounts根本不会被调用。
修复步骤
1. 提前注册观察者
把Profile控制器里的观察者注册移到更早的生命周期方法,比如viewDidLoad,这个方法在控制器创建后立即执行,早于通知发送的时机:
override func viewDidLoad() { super.viewDidLoad() // 在这里注册观察者,确保通知发送前已经完成注册 NotificationCenter.default.addObserver( self, selector: #selector(changeCounts), name: NSNotification.Name("com.rjt.Chao.mypostnumbercount"), object: nil ) } // 务必添加deinit方法移除观察者,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) }
2. 优化代码安全性
- 避免强制解包,在
changeCounts里用更安全的类型转换:
@objc func changeCounts(notification: Notification) { guard let count = notification.userInfo?["currentUserPostCount"] as? Int else { print("无法解析帖子数量") return } posts.setTitle("\(count)", for: .normal) }
- 用
extension定义通知名称,避免字符串拼写错误:
extension Notification.Name { static let myPostNumberCount = Notification.Name("com.rjt.Chao.mypostnumbercount") static let postsClicked = Notification.Name("com.rjt.Chao.postsCliked") }
之后使用时直接写.myPostNumberCount,比如:
// Home控制器发送通知 NotificationCenter.default.post( name: .myPostNumberCount, object: self, userInfo: ["currentUserPostCount": count] ) // Profile控制器注册观察者 NotificationCenter.default.addObserver( self, selector: #selector(changeCounts), name: .myPostNumberCount, object: nil )
更优雅的双向数据传递方案
虽然NotificationCenter能解决问题,但对于两个TabBar控制器之间的直接数据传递,还有更合适的方案:
- 共享ViewModel:创建一个单例或注入的
PostDataViewModel,用@Published属性(Combine框架)存储帖子数量,两个控制器都订阅这个属性的变化,自动更新UI。 - Delegate模式:让TabBarController作为中间代理,或者两个控制器互相持有weak delegate引用,直接调用方法传递数据。
- 闭包回调:在Profile控制器初始化时,传入一个获取帖子数量的闭包,或者Home控制器把更新数量的闭包传给Profile。
这些方案比NotificationCenter更直接,也更容易维护和调试。
内容的提问来源于stack exchange,提问作者Chao Peng
相关产品推荐
相关产品推荐

