SwiftUI中ObservableObject跨实例修改属性的问题求助
解决SwiftUI中两个ObservableObject的交互问题
错误原因分析
- 第一种错误:直接在
LoginViewModel中实例化AppSettings会创建全新的独立实例,和MyApp中全局的@StateObject实例不是同一个,修改该实例的isLogin状态不会同步到ContentView的判断逻辑。 - 第二种错误:
@EnvironmentObject只能用于View结构体,无法直接在ObservableObject类中使用,因此无法通过该方式获取全局的AppSettings实例。
解决方案:依赖注入传递AppSettings
通过将全局的AppSettings实例注入到LoginViewModel中,实现两个ObservableObject的状态同步。
1. 修改LoginViewModel,添加初始化参数接收AppSettings
class LoginViewModel: ObservableObject { @Published var login = "" @Published var password = "" // 持有全局的AppSettings实例 private let appSettings: AppSettings // 初始化时传入AppSettings init(appSettings: AppSettings) { self.appSettings = appSettings } func auth() { UserAPI().Auth(req: LoginRequest(email: login, password: password)) { response, error in if let err = error { // 错误处理逻辑 } else if let response = response { Defaults.accessToken = response.tokens.accessToken Defaults.refreshToken = response.tokens.refreshToken // 切换到主线程更新状态(网络请求在后台线程) DispatchQueue.main.async { self.appSettings.isLogin = true } } } } }
2. 修改LoginView,注入AppSettings并初始化ViewModel
struct LoginView: View { @EnvironmentObject var appSettings: AppSettings @StateObject var vm: LoginViewModel // 自定义初始化方法,接收AppSettings实例 init(appSettings: AppSettings) { _vm = StateObject(wrappedValue: LoginViewModel(appSettings: appSettings)) } var body: some View { NavigationView { VStack { TextField("Email", text: $vm.login) .textFieldStyle(.roundedBorder) .padding() TextField("Password", text: $vm.password) .textFieldStyle(.roundedBorder) .padding() .secureFieldStyle() Button { vm.auth() } label: { Text("SignIn") .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } } .padding() } } }
3. 修改ContentView,传递AppSettings到LoginView
struct ContentView: View { @EnvironmentObject var vm: AppSettings var body: some View { if vm.isLogin { MainView(appSettings: vm) .environmentObject(vm) } else { // 将全局的AppSettings实例传入LoginView LoginView(appSettings: vm) .environmentObject(vm) } } }
登出功能实现(同理)
在主页面的ViewModel中同样通过依赖注入获取AppSettings,修改isLogin状态即可:
class MainViewModel: ObservableObject { private let appSettings: AppSettings init(appSettings: AppSettings) { self.appSettings = appSettings } func logout() { // 清除token Defaults.accessToken = nil Defaults.refreshToken = nil // 切换到主线程更新状态 DispatchQueue.main.async { self.appSettings.isLogin = false } } } struct MainView: View { @EnvironmentObject var appSettings: AppSettings @StateObject var vm: MainViewModel init(appSettings: AppSettings) { _vm = StateObject(wrappedValue: MainViewModel(appSettings: appSettings)) } var body: some View { TabView { Text("Home") .tabItem { Image(systemName: "house") Text("Home") } Button("Logout") { vm.logout() } .tabItem { Image(systemName: "person.crop.circle") Text("Profile") } } } }
内容的提问来源于stack exchange,提问作者Ilia Ivanchik
相关产品推荐
相关产品推荐

