嵌入NSHostingView后SwiftUI View无法更新的问题排查
macOS菜单栏SwiftUI视图不更新的排查方案
1. 确认Observable实例全局唯一
如果你的MainModel在多处被初始化,菜单栏视图绑定的实例很可能不是Timer正在更新的那个。要保证整个App共用同一个实例:
@main struct YourApp: App { @State private var mainModel = MainModel() var body: some Scene { MenuBarExtra("App", systemImage: "timer") { MenuView(mainModel: mainModel) } .onAppear { mainModel.startTimer() } } }
2. 必须在主线程更新Observable属性
Timer如果在后台线程触发,更新属性时SwiftUI无法感知到变化。一定要把属性更新逻辑切到主线程:
@Observable class MainModel { var upper = 0 var lower = 0 private var timer: Timer? func startTimer() { timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in DispatchQueue.main.async { self?.upper += 1 self?.lower -= 1 } } } }
3. 不要用本地State切断绑定
如果菜单栏视图里把mainModel的属性赋值给本地@State,会断开和原对象的绑定,自然不会更新:
// 错误写法 struct MenuView: View { let mainModel: MainModel @State private var localUpper = 0 var body: some View { Text("Upper: \(localUpper)") .onAppear { localUpper = mainModel.upper } } } // 正确写法:直接引用Model的属性 struct MenuView: View { let mainModel: MainModel var body: some View { VStack { Text("Upper: \(mainModel.upper)") Text("Lower: \(mainModel.lower)") } } }
4. 极端情况:手动触发视图刷新
如果以上方法都没效果,可以通过通知强制触发视图刷新:
@Observable class MainModel { var upper = 0 { didSet { NotificationCenter.default.post(name: .refreshMenu, object: nil) } } var lower = 0 { didSet { NotificationCenter.default.post(name: .refreshMenu, object: nil) } } // ...Timer代码 } // 扩展通知名 extension Notification.Name { static let refreshMenu = Notification.Name("RefreshMenu") } // 菜单栏视图监听通知 struct MenuView: View { let mainModel: MainModel @State private var refreshFlag = 0 var body: some View { VStack { Text("Upper: \(mainModel.upper)") Text("Lower: \(mainModel.lower)") } .onReceive(NotificationCenter.default.publisher(for: .refreshMenu)) { _ in refreshFlag += 1 } } }
内容的提问来源于stack exchange,提问作者inexcitus
相关产品推荐
相关产品推荐

