SwiftUI MVVM中同页面ViewModel间通信的实现方案
SwiftUI中实现两个ViewModel状态同步的方案
针对你提出的ToolbarViewModel与MainViewModel同步selectedTool的需求,以下是几种实用的实现方式:
方案一:抽离共享状态数据源(推荐)
将工具选择的状态独立为一个共享数据源,让两个ViewModel都依赖该数据源,实现状态的统一管理与同步。
代码实现
- 定义共享状态类:
@MainActor final class ToolSelectionStore: ObservableObject { @Published var selectedTool: Int = 1 func selectTool(_ tool: Int) { selectedTool = tool } }
- 修改两个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 } }
- 父视图中创建数据源并传递给子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监听状态变化并完成同步。
代码实现
- 修改子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 }
- 父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) } }
- 父视图使用父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直接访问。
代码实现
- 定义共享状态类(同方案一的
ToolSelectionStore):
@MainActor final class ToolSelectionStore: ObservableObject { @Published var selectedTool: Int = 1 func selectTool(_ tool: Int) { selectedTool = tool } }
- 修改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 } }
- 父视图注入环境对象:
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
相关产品推荐
相关产品推荐

