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

SwiftUI中不同ViewModel间的通信实现方案咨询

解决方案

当然可以实现两个ViewModel之间的通信,下面是几种实用的方案:

方案1:让ViewModel_2持有ViewModel_1的引用

这是最直接的实现方式,在父视图初始化时将ViewModel_1的实例传入ViewModel_2,让后者能直接调用前者的方法修改数据。

修改后的代码示例:

// 更新ViewModel_2,添加对ViewModel_1的引用
class ViewModel_2: ObservableObject {
    private let viewModel1: ViewModel_1
    
    init(viewModel1: ViewModel_1) {
        self.viewModel1 = viewModel1
    }
    
    func changeValue_in_ViewModel_1(value: Int) {
        viewModel1.changeValue(value: value)
    }
}

// 更新ParentView的初始化逻辑
struct ParentView: View {
    @StateObject var viewModel_1: ViewModel_1
    @StateObject var viewModel_2: ViewModel_2
    
    // 便捷初始化方法,关联两个ViewModel
    init() {
        let vm1 = ViewModel_1()
        _viewModel_1 = StateObject(wrappedValue: vm1)
        _viewModel_2 = StateObject(wrappedValue: ViewModel_2(viewModel1: vm1))
    }
    
    var body: some View {
        ZStack {
            ChildView_1()
                .environmentObject(viewModel_1)
            
            ChildView_2()
                .environmentObject(viewModel_2)
        }
    }
}

方案2:用Combine实现解耦式通信

如果希望两个ViewModel保持低耦合,可以利用Combine的发布订阅机制传递修改信号:

import Combine

// 给ViewModel_2添加事件发布者
class ViewModel_2: ObservableObject {
    let valueChangeRequest = PassthroughSubject<Int, Never>()
}

// 让ViewModel_1订阅ViewModel_2的事件
class ViewModel_1: ObservableObject {
    @Published var someValue: Int = 0
    private var cancellables = Set<AnyCancellable>()
    
    func subscribeToValueChanges(from viewModel2: ViewModel_2) {
        viewModel2.valueChangeRequest
            .sink { [weak self] newValue in
                self?.changeValue(value: newValue)
            }
            .store(in: &cancellables)
    }
    
    func changeValue(value: Int) {
        self.someValue = value
    }
}

// 在父视图中建立订阅关系
struct ParentView: View {
    @StateObject var viewModel_1: ViewModel_1
    @StateObject var viewModel_2: ViewModel_2
    
    init() {
        let vm1 = ViewModel_1()
        let vm2 = ViewModel_2()
        vm1.subscribeToValueChanges(from: vm2)
        _viewModel_1 = StateObject(wrappedValue: vm1)
        _viewModel_2 = StateObject(wrappedValue: vm2)
    }
    
    var body: some View {
        ZStack {
            ChildView_1()
                .environmentObject(viewModel_1)
            
            ChildView_2()
                .environmentObject(viewModel_2)
        }
    }
}

// 修改ChildView_2的按钮动作,触发事件发布
struct ChildView_2: View {
    @EnvironmentObject var viewModel_2: ViewModel_2
    
    var body: some View {
        Button(action: {
            viewModel_2.valueChangeRequest.send(1)
        }, label: {
            Text("Tap to change value")
        })
    }
}

方案3:通过父视图中转通信

如果需要完全隔离两个ViewModel,可以把通信逻辑放在父视图层,让子视图通过闭包通知父视图,再由父视图操作目标ViewModel:

// 修改ChildView_2,添加回调闭包
struct ChildView_2: View {
    var onValueChange: (Int) -> Void
    
    var body: some View {
        Button(action: {
            onValueChange(1)
        }, label: {
            Text("Tap to change value")
        })
    }
}

// 在ParentView中传递回调,操作ViewModel_1
struct ParentView: View {
    @StateObject var viewModel_1: ViewModel_1 = ViewModel_1()
    @StateObject var viewModel_2: ViewModel_2 = ViewModel_2()
    
    var body: some View {
        ZStack {
            ChildView_1()
                .environmentObject(viewModel_1)
            
            ChildView_2(onValueChange: { newValue in
                viewModel_1.changeValue(value: newValue)
            })
        }
    }
}

三种方案的适用场景:

  • 方案1适合简单业务场景,逻辑直接但耦合度略高
  • 方案2适合复杂项目,遵循响应式编程风格,实现低耦合
  • 方案3适合需要严格隔离ViewModel的场景,将通信逻辑收拢到视图层

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:03:18