如何在两个ViewModel间同步@Published属性用于SwiftUI视图绑定
解决两个ViewModel的@Published属性双向同步问题
你遇到的错误是因为直接将Binding<String?>类型的$vm.myProperty传给了期望String?类型参数的SecondViewModel初始化方法,同时当前代码无法实现两个ViewModel属性的双向同步。
核心解决方案
让SecondViewModel通过接收外部Binding实现与外部数据的双向绑定,无需知晓FirstViewModel的存在;利用Combine框架在SecondViewModel内部建立自身@Published属性与外部Binding的双向订阅,确保两者值始终一致。
修改后的完整代码
import SwiftUI import Combine class FirstViewModel: ObservableObject { @Published var myProperty: String? private var cancellables = Set<AnyCancellable>() init() { self.myProperty = "Two" } } struct FirstView: View { @EnvironmentObject var vm: FirstViewModel var body: some View { SecondView() .environmentObject(SecondViewModel(externalProperty: $vm.myProperty)) } } class SecondViewModel: ObservableObject { @Published var myProperty: String? private var externalProperty: Binding<String?> private var cancellables = Set<AnyCancellable>() // 接收外部Binding,而非具体值 init(externalProperty: Binding<String?>) { self.externalProperty = externalProperty // 1. 自身myProperty变化时,同步更新外部Binding $myProperty .sink { [weak self] newValue in guard let self = self else { return } // 避免循环触发,仅值不同时更新 if newValue != self.externalProperty.wrappedValue { self.externalProperty.wrappedValue = newValue } } .store(in: &cancellables) // 2. 外部Binding值变化时,同步更新自身myProperty externalProperty .projectedValue .sink { [weak self] newValue in guard let self = self else { return } if newValue != self.myProperty { self.myProperty = newValue } } .store(in: &cancellables) // 初始化时同步外部值到自身 self.myProperty = externalProperty.wrappedValue } } struct SecondView: View { @EnvironmentObject var vm: SecondViewModel let items: [String] = ["One", "Two", "Three"] var body: some View { Picker( selection: $vm.myProperty, label: EmptyView() ) { ForEach(items, id: \.self) { item in Text(item).tag(item as String?) } } } }
关键修改说明
- SecondViewModel初始化:改为接收
Binding<String?>类型参数,彻底解耦与FirstViewModel的依赖 - 双向订阅逻辑:
- 订阅自身
$myProperty的变化,同步更新外部Binding的值,加入值判断避免循环触发 - 订阅外部Binding的
projectedValue(Binding的发布者),同步更新自身myProperty
- 订阅自身
- SecondView的Picker:给
tag添加as String?类型标注,因为myProperty是可选类型,确保Picker选择类型匹配
内容的提问来源于stack exchange,提问作者Serhii R
相关产品推荐
相关产品推荐

