You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:42:30