如何实现可在ViewModel中调用切换SwiftUI Tab页的NavigationManager
问题根因
你当前代码失效的核心原因是:每个ViewModel内部都单独初始化了全新的NavigationManager实例,和ContentView中创建的全局管控Tab状态的实例不是同一个对象,因此修改ViewModel内的实例不会触发TabView的选中状态变化。
实现方案
下面提供两种无需通过ViewModel初始化方法传参的实现方案:
方案1:单例模式(推荐,改动最小)
直接给NavigationManager配置全局唯一的共享实例,所有场景都复用这一个实例即可。
第一步:修改NavigationManager代码
class NavigationManager: ObservableObject { // 全局共享单例 static let shared = NavigationManager() @Published var selection: NavigationItem = .account // 私有化初始化方法,禁止外部手动创建新实例 private init() {} }
第二步:修改ViewModel中的引用
class AccountViewModel: ObservableObject { // 直接引用全局单例,无需通过init传入 let navigationManager = NavigationManager.shared } class SettingsViewModel: ObservableObject { let navigationManager = NavigationManager.shared }
第三步:修改ContentView中的实例引用,并补全环境对象注入
你之前的代码只给SettingsView注入了环境对象,需要同时给AccountView也注入才能保证视图层直接使用环境对象时也能正常生效:
struct ContentView: View { @StateObject var navigationManager = NavigationManager.shared var body: some View { TabView(selection: $navigationManager.selection) { AccountView() .tabItem { Text("Account") Image(systemName: "person.crop.circle") } .tag(NavigationItem.account) .environmentObject(navigationManager) SettingsView() .tabItem { Text("Settings") Image(systemName: "gear") } .tag(NavigationItem.settings) .environmentObject(navigationManager) } } }
方案2:视图层属性注入(无需使用单例)
如果你不想使用单例,可以在视图层拿到环境对象后,直接赋值给ViewModel的对应属性,不需要修改ViewModel的初始化方法:
第一步:修改ViewModel的属性定义
class AccountViewModel: ObservableObject { var navigationManager: NavigationManager! }
第二步:在视图层完成属性赋值
struct AccountView: View { @EnvironmentObject var navigationManager: NavigationManager @StateObject var viewModel = AccountViewModel() var body: some View { VStack(spacing: 16) { Text("AccountView") .font(.title3) Button(action: { viewModel.navigationManager.selection = .settings }) { Text("Go to Settings tab") .font(.headline) } .buttonStyle(.borderedProminent) } .onAppear { // 页面首次出现时将全局环境对象赋值给ViewModel viewModel.navigationManager = navigationManager } } }
内容的提问来源于stack exchange,提问作者Roland Lariotte
相关产品推荐
相关产品推荐

