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

SwiftUI+MVVM中如何在@Observable视图模型中使用绑定?

解决SwiftUI MVVM中多视图状态同步问题

你的问题核心在于子ViewModel接收的是主ViewModel属性的拷贝值,而非双向绑定的引用:

  • 主视图点击按钮时,MainViewModel.sourceToggle变化会触发MainView的body刷新,此时会重新初始化SubViewModel并传入新的sourceToggle值,所以子视图文本会更新;
  • 但子视图点击按钮时,修改的只是SubViewModel自己的bindingToggle变量,和MainViewModel的数据源完全无关,因此主视图不会同步变化。

下面提供几种可行的解决方案:

方案1:通过Binding传递双向绑定关系

让子ViewModel持有主ViewModel属性的Binding,直接操作原数据源:

修改SubViewModel

@Observable
class SubViewModel {
    let bindingToggle: Binding<Bool>
    
    init(bindingToggle: Binding<Bool>) {
        self.bindingToggle = bindingToggle
    }
    
    func toggle() {
        bindingToggle.wrappedValue.toggle()
    }
}

修改MainView中SubView的初始化

struct MainView: View {
    var viewModel = MainViewModel()
    
    var body: some View {
        Text(viewModel.sourceToggle ? "isOn" : "isOff")
        Button("Toggle1") {
            viewModel.sourceToggle.toggle()
        }
        // 传递sourceToggle的Binding
        SubView(viewModel: SubViewModel(bindingToggle: $viewModel.sourceToggle))
    }
}

修改SubView的按钮操作

struct SubView: View {
    @Bindable var viewModel: SubViewModel
    
    var body: some View {
        Text(viewModel.bindingToggle.wrappedValue ? "isOn" : "isOff")
        Button("Toggle2") {
            viewModel.toggle()
        }
    }
}

方案2:子ViewModel持有主ViewModel引用(更符合MVVM共享数据源思路)

让子ViewModel直接依赖主ViewModel,通过访问主ViewModel的属性实现状态同步:

修改SubViewModel

@Observable
class SubViewModel {
    private let mainViewModel: MainViewModel
    
    // 通过计算属性代理主ViewModel的sourceToggle
    var bindingToggle: Bool {
        get { mainViewModel.sourceToggle }
        set { mainViewModel.sourceToggle = newValue }
    }
    
    init(mainViewModel: MainViewModel) {
        self.mainViewModel = mainViewModel
    }
    
    func toggle() {
        mainViewModel.sourceToggle.toggle()
    }
}

修改MainView中SubView的初始化

struct MainView: View {
    var viewModel = MainViewModel()
    
    var body: some View {
        Text(viewModel.sourceToggle ? "isOn" : "isOff")
        Button("Toggle1") {
            viewModel.sourceToggle.toggle()
        }
        // 传入主ViewModel实例
        SubView(viewModel: SubViewModel(mainViewModel: viewModel))
    }
}

SubView无需额外修改

struct SubView: View {
    @Bindable var viewModel: SubViewModel
    
    var body: some View {
        Text(viewModel.bindingToggle ? "isOn" : "isOff")
        Button("Toggle2") {
            viewModel.toggle()
            // 或者直接修改:viewModel.bindingToggle.toggle()
        }
    }
}

方案3:简化实现——直接给子视图传递Binding(适合子视图逻辑简单的场景)

如果子视图不需要复杂的ViewModel逻辑,可以直接跳过SubViewModel,让子视图接收Binding:

修改SubView

struct SubView: View {
    @Binding var toggleValue: Bool
    
    var body: some View {
        Text(toggleValue ? "isOn" : "isOff")
        Button("Toggle2") {
            toggleValue.toggle()
        }
    }
}

修改MainView中SubView的调用

struct MainView: View {
    var viewModel = MainViewModel()
    
    var body: some View {
        Text(viewModel.sourceToggle ? "isOn" : "isOff")
        Button("Toggle1") {
            viewModel.sourceToggle.toggle()
        }
        SubView(toggleValue: $viewModel.sourceToggle)
    }
}

以上三种方案都能实现主、子视图的状态双向同步,你可以根据子视图的业务复杂度选择合适的方案。

内容的提问来源于stack exchange,提问作者Max

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 16:10:59