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

SwiftUI MVVM中同页面ViewModel间通信的实现方案

SwiftUI中实现两个ViewModel状态同步的方案

针对你提出的ToolbarViewModel与MainViewModel同步selectedTool的需求,以下是几种实用的实现方式:

方案一:抽离共享状态数据源(推荐)

将工具选择的状态独立为一个共享数据源,让两个ViewModel都依赖该数据源,实现状态的统一管理与同步。

代码实现

  1. 定义共享状态类:
@MainActor final class ToolSelectionStore: ObservableObject {
    @Published var selectedTool: Int = 1
    
    func selectTool(_ tool: Int) {
        selectedTool = tool
    }
}
  1. 修改两个ViewModel,依赖共享数据源:
@MainActor final class ToolbarViewModel: ObservableObject {
    private let toolStore: ToolSelectionStore
    
    init(toolStore: ToolSelectionStore) {
        self.toolStore = toolStore
    }
    
    func selectTool() {
        toolStore.selectTool(2)
    }
}

@MainActor final class MainViewModel: ObservableObject {
    private let toolStore: ToolSelectionStore
    // 直接同步共享数据源的状态
    var selectedTool: Int { toolStore.selectedTool }
    
    init(toolStore: ToolSelectionStore) {
        self.toolStore = toolStore
    }
}
  1. 父视图中创建数据源并传递给子View:
struct ParentView: View {
    @StateObject private var toolStore = ToolSelectionStore()
    
    var body: some View {
        VStack {
            ToolbarView(VM: ToolbarViewModel(toolStore: toolStore))
            MainView(VM: MainViewModel(toolStore: toolStore), toolStore: toolStore)
        }
    }
}

// 修改子View,接收外部传入的ViewModel和数据源
struct ToolbarView: View {
    @ObservedObject var VM: ToolbarViewModel
    
    var body: some View {
        Button("切换工具") {
            VM.selectTool()
        }
    }
}

struct MainView: View {
    @ObservedObject var VM: MainViewModel
    @ObservedObject var toolStore: ToolSelectionStore
    
    init(VM: MainViewModel, toolStore: ToolSelectionStore) {
        self.VM = VM
        self.toolStore = toolStore
    }
    
    var body: some View {
        Text("当前选中工具:\(toolStore.selectedTool)")
    }
}

方案二:通过父ViewModel管理同步逻辑

创建父ViewModel持有两个子ViewModel,利用Combine监听状态变化并完成同步。

代码实现

  1. 修改子ViewModel,将selectedTool改为@Published:
@MainActor final class ToolbarViewModel: ObservableObject {
    @Published var selectedTool: Int = 1
    
    func selectTool() {
        selectedTool = 2
    }
}

@MainActor final class MainViewModel: ObservableObject {
    @Published var selectedTool: Int = 1
}
  1. 父ViewModel处理同步:
@MainActor final class ParentViewModel: ObservableObject {
    let toolbarVM = ToolbarViewModel()
    let mainVM = MainViewModel()
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // 监听ToolbarViewModel的状态变化,同步到MainViewModel
        toolbarVM.$selectedTool
            .assign(to: \.selectedTool, on: mainVM)
            .store(in: &cancellables)
    }
}
  1. 父视图使用父ViewModel:
struct ParentView: View {
    @StateObject private var parentVM = ParentViewModel()
    
    var body: some View {
        VStack {
            ToolbarView(VM: parentVM.toolbarVM)
            MainView(VM: parentVM.mainVM)
        }
    }
}

struct ToolbarView: View {
    @ObservedObject var VM: ToolbarViewModel
    
    var body: some View {
        Button("切换工具") {
            VM.selectTool()
        }
    }
}

struct MainView: View {
    @ObservedObject var VM: MainViewModel
    
    var body: some View {
        Text("当前选中工具:\(VM.selectedTool)")
    }
}

方案三:使用EnvironmentObject传递共享状态

将共享状态注入视图环境,让子View和ViewModel直接访问。

代码实现

  1. 定义共享状态类(同方案一的ToolSelectionStore):
@MainActor final class ToolSelectionStore: ObservableObject {
    @Published var selectedTool: Int = 1
    
    func selectTool(_ tool: Int) {
        selectedTool = tool
    }
}
  1. 修改ViewModel依赖环境对象:
@MainActor final class ToolbarViewModel: ObservableObject {
    @EnvironmentObject private var toolStore: ToolSelectionStore
    
    func selectTool() {
        toolStore.selectTool(2)
    }
}

@MainActor final class MainViewModel: ObservableObject {
    @EnvironmentObject private var toolStore: ToolSelectionStore
    var selectedTool: Int { toolStore.selectedTool }
}
  1. 父视图注入环境对象:
struct ParentView: View {
    @StateObject private var toolStore = ToolSelectionStore()
    
    var body: some View {
        VStack {
            ToolbarView()
            MainView()
        }
        .environmentObject(toolStore)
    }
}

struct ToolbarView: View {
    @StateObject private var VM = ToolbarViewModel()
    
    var body: some View {
        Button("切换工具") {
            VM.selectTool()
        }
    }
}

struct MainView: View {
    @StateObject private var VM = MainViewModel()
    @EnvironmentObject private var toolStore: ToolSelectionStore
    
    var body: some View {
        Text("当前选中工具:\(toolStore.selectedTool)")
    }
}

方案对比

  • 方案一:符合单一职责原则,状态集中管理,扩展性强,适合中大型项目。
  • 方案二:逻辑集中在父ViewModel,实现简单,适合小型项目,但状态增多后易导致父ViewModel臃肿。
  • 方案三:适合跨多层View共享状态,但需注意环境对象的作用域,避免滥用导致状态追踪困难。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 22:45:38