SwiftUI:无需ViewController,为各视图配置独立EnvironmentObject(含VM与Interactor)
摆脱ViewController依赖,为SwiftUI视图配置独立的ViewModel+Interactor组合对象
我来帮你梳理下怎么移除对ViewController的依赖,同时让每个SwiftUI视图实例都拥有专属的ViewModel和Interactor组合对象,完全基于SwiftUI的生命周期来管理。
核心思路
我们要抛弃原来通过ViewController传递组合对象的方式,转而利用SwiftUI的@StateObject属性包装器——它会确保每个视图实例都拥有独立的对象实例,并且在视图的整个生命周期内保持存活。同时,我们需要重构Interactor,去掉对UIViewController的依赖,改用SwiftUI原生的导航机制来处理页面跳转。
步骤1:重构Interactor,移除UIViewController依赖
原来的Interactor依赖VC来做页面跳转,现在我们可以用回调闭包来通知视图执行导航操作,把具体的跳转逻辑交还给SwiftUI视图处理:
import SwiftUI import Combine class ViewModel1: ObservableObject { @Published var text: String init(text: String) { self.text = text } } class Interactor1: ObservableObject { let viewModel: ViewModel1 // 定义导航回调,由视图实现具体跳转逻辑 var onButtonClicked: (() -> Void)? init(viewModel: ViewModel1) { self.viewModel = viewModel } func buttonClicked() { // 触发回调,让视图处理导航 onButtonClicked?() } }
步骤2:重新设计CombinedObject
不再需要传入ViewController参数,直接在初始化时创建对应的ViewModel和Interactor,同时监听两者的变化并转发给组合对象的objectWillChange,确保视图能感知到内部数据的更新:
class CombinedObject: ObservableObject { @Published var interactor: Interactor1 @Published var viewModel: ViewModel1 // 用Set来管理订阅,避免内存泄漏 private var cancellables = Set<AnyCancellable>() init(initialText: String) { self.viewModel = ViewModel1(text: initialText) self.interactor = Interactor1(viewModel: viewModel) // 转发Interactor的变化 interactor.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) // 转发ViewModel的变化 viewModel.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } }
步骤3:在SwiftUI视图中使用@StateObject创建独立组合对象
每个视图通过@StateObject初始化自己的CombinedObject,确保实例独立。同时在视图中处理Interactor的导航回调,用SwiftUI原生组件(比如sheet、NavigationStack)实现页面跳转:
struct View1: View { // 每个View1实例都会拥有专属的CombinedObject @StateObject private var combinedObject = CombinedObject(initialText: "text1") @State private var showSecondView = false var body: some View { VStack(spacing: 20) { Button(action: { combinedObject.interactor.buttonClicked() }) { Text(combinedObject.viewModel.text) .font(.title) } } .onAppear { // 给Interactor设置导航回调 combinedObject.interactor.onButtonClicked = { showSecondView = true } } // 用SwiftUI的sheet实现页面跳转 .sheet(isPresented: $showSecondView) { View2() } } } // 示例第二个视图,同样拥有独立的组合对象 struct View2: View { @StateObject private var combinedObject = CombinedObject(initialText: "text2") var body: some View { VStack { Text(combinedObject.viewModel.text) .font(.title) Button("返回") { // 利用环境中的dismiss动作关闭sheet dismiss() } } .environment(\.dismiss, DismissAction()) } }
关键优势
- 完全摆脱ViewController依赖:整个架构基于SwiftUI的生命周期,不需要UIViewController作为中介。
- 视图实例独立:每个视图的CombinedObject、ViewModel、Interactor都是独立的,不会出现状态共享混乱的问题。
- 符合SwiftUI设计理念:导航逻辑由SwiftUI原生组件处理,数据更新通过
ObservableObject和属性包装器自动同步到视图。
如果你需要在多个视图间共享某些全局状态,可以在父视图创建对应的对象并通过environmentObject(_:)传递,但对于每个视图的专属状态,@StateObject是最优选择。
内容的提问来源于stack exchange,提问作者DevB2F
相关产品推荐
相关产品推荐

