如何让@Observable的CarViewModel绑定@ObservableObject的Car模型更新视图?
解决@Observable视图模型跟随ObservableObject模型同步的问题
需求说明
需要让使用@Observable的CarViewModel的rentDate属性,动态绑定到ObservableObject类型的Car1或Car2的rentDate属性。点击"Bind to Car1"后,修改Car1的日期时,视图中的Text能同步更新;点击"Bind to Car2"后同理,实现视图模型适配不同数据源的动态绑定逻辑。
实现方案
核心思路是利用Combine框架订阅ObservableObject的@Published属性变化,同步更新@Observable视图模型的属性,同时支持切换数据源时取消旧订阅、建立新订阅。
步骤1:定义统一协议规范数据源
为Car1和Car2定义共同协议,让视图模型可以统一处理不同的数据源:
import Combine import SwiftUI import Observation protocol HasRentDate: ObservableObject { var rentDate: Date { get set } }
步骤2:修改数据源类遵守协议
让Car1和Car2遵守上述协议:
class Car1: HasRentDate { @Published var rentDate: Date = .now } class Car2: HasRentDate { @Published var rentDate: Date = .now }
步骤3:改造CarViewModel添加绑定逻辑
在CarViewModel中添加Combine订阅逻辑,实现动态绑定数据源并同步值:
@Observable class CarViewModel { var rentDate: Date = .now private var cancellable: AnyCancellable? // 绑定到指定数据源,自动同步rentDate变化 func bind(to car: some HasRentDate) { // 取消之前的订阅,避免内存泄漏 cancellable?.cancel() // 订阅新数据源的rentDate变化,同步到自身 cancellable = car.$rentDate .assign(to: \.rentDate, on: self) // 初始同步一次当前值 rentDate = car.rentDate } }
步骤4:更新视图中的绑定逻辑
修改ContentView的按钮点击事件,调用bind方法完成动态绑定:
struct ContentView: View { @StateObject var car1 = Car1() @StateObject var car2 = Car2() @State var model = CarViewModel() var body: some View { NavigationStack { VStack { Spacer() HStack { Spacer() Button { model.bind(to: car1) } label: { Text("Bind to Car1") } Spacer() Button { model.bind(to: car2) } label: { Text("Bind to Car2") } Spacer() } Spacer() HStack { Spacer() Button { car1.rentDate = .now } label: { Text("Change Car1") } Spacer() Button { car2.rentDate = .now } label: { Text("Change Car2") } Spacer() } Spacer() Text("DATE: \(model.rentDate)") Spacer() } } } } struct CarView: View { @State var model = CarViewModel() var body: some View { Text("DATE: \(model.rentDate)") } } #Preview { ContentView() }
工作原理
- 点击"Bind to CarX"按钮时,
CarViewModel会取消之前的订阅(如果有),然后订阅目标Car的$rentDate(@Published生成的Publisher)。 - 当
Car的rentDate更新时,Publisher会发送新值,通过assign方法自动同步到CarViewModel的rentDate属性。 - 由于
CarViewModel使用@Observable,其rentDate变化会自动触发视图刷新,实现UI同步更新。
内容的提问来源于stack exchange,提问作者zumzum
相关产品推荐
相关产品推荐

