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

如何让@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:25:55