就一个常见的需求,启动app时先进入LoginView,登录后进入MainView,退出登录后回到
LoginView。
一、使用环境共享状态的方式
在一个视图中使用 @StateObject
声明并初始化一个状态对象(如 rootViewManager
),然后通过 @EnvironmentObject
将其注入到视图环境中。在同一个视图树中的其他视图中,可以使用 @EnvironmentObject
来访问这个状态对象。
关键点
-
@StateObject
的作用:@StateObject
用于在视图中创建和拥有一个状态对象的实例。它确保对象的生命周期与视图绑定,并且在视图重新加载时不会重新创建。@StateObject
只能用于对象的“创建者”视图。
-
@EnvironmentObject
的作用:@EnvironmentObject
是 SwiftUI 的依赖注入机制,用于在视图树中共享状态对象。- 通过
@EnvironmentObject
声明的属性,必须由父视图通过.environmentObject()
注入,否则会导致运行时崩溃。
-
视图树中状态共享的前提:
- 使用
@EnvironmentObject
的视图,必须位于声明并注入该对象的视图树内。
- 使用
1. 定义状态对象
创建一个 RootViewManager
类,继承自 ObservableObject
:
import SwiftUIclass RootViewManager: ObservableObject {enum RootView {case logincase main}@Published var currentRootView: RootView = .main
}
2. 在主视图中声明 @StateObject
并注入环境
在主视图中使用 @StateObject
创建并管理 RootViewManager
的实例,同时通过 .environmentObject()
将其注入到视图树中。
import SwiftUI@main
struct MyApp: App {@StateObject private var rootViewManager = RootViewManager() // 创建状态对象var body: some Scene {WindowGroup {RootViewSwitcher() // 根视图.environmentObject(rootViewManager) // 注入状态对象到环境中}}
}
根视图中处理切换逻辑
import SwiftUIstruct RootViewSwitcher: View {@EnvironmentObject var rootViewManager: RootViewManagervar body: some View {Group {switch rootViewManager.currentRootView {case .login:LoginView()case .main:MainView()}}}
}
3. 在子视图中通过 @EnvironmentObject
获取状态
在其他视图中,通过 @EnvironmentObject
获取注入的 RootViewManager
,并根据其状态决定显示什么内容。
示例:登录视图
struct LoginView: View {@EnvironmentObject var rootViewManager: RootViewManagervar body: some View {VStack {Text("登录页面")Button("进入主界面") {rootViewManager.currentRootView = .main // 切换到主视图}}}
}
示例:主视图
struct MainView: View {@EnvironmentObject var rootViewManager: RootViewManagervar body: some View {VStack {Text("主界面")Button("退出登录") {rootViewManager.currentRootView = .login // 切换到登录视图}}}
}
二、使用单例状态对象的方式
如果希望整个项目中全局访问RootViewManager
,可以将它设置为单例。
1. 定义状态对象单例
import SwiftUIclass RootViewManager: ObservableObject {static let shared = RootViewManager() // 单例enum RootView {case logincase main}@Published var currentRootView: RootView = .main
}
2. 在主视图中注入单例
import SwiftUI@main
struct MyApp: App {var body: some Scene {WindowGroup {RootViewSwitcher().environmentObject(RootViewManager.shared) // 注入单例}}
}
根视图中处理切换逻辑
import SwiftUIstruct RootViewSwitcher: View {@EnvironmentObject var rootViewManager: RootViewManagervar body: some View {Group {switch rootViewManager.currentRootView {case .login:LoginView()case .main:TabbarView()}}}
}
3.在其他所有类中都可以获取和改变状态
class Tools {/// 切换到主界面static func enterMainView() {RootViewManager.shared.currentRootView = .main}/// 切换到登录界面static func enterLoginView() {RootViewManager.shared.currentRootView = .login}
}
三、使用@AppStorage属性包装器的方式
1.在主视图使用@AppStorage监听UserDefaults中特定键的变化
import SwiftUI@main
struct MyApp: App {/*使用 @AppStorage 属性包装器可以方便地监听 UserDefaults 中特定键的变化。当使用 @AppStorage 来包装一个变量时,它会自动监听 UserDefaults 中与该变量关联键的变化,并在值改变时更新界面。*/@AppStorage("isLogin") var isLogin: Bool = falsevar body: some Scene {WindowGroup {if isLogin {MainView()} else {LoginView()} }}
}
2.登录页登录
import SwiftUIstruct LoginView: View {@AppStorage("isLogin") var isLogin: Bool = falsevar body: some View {VStack {Text("登录页面")Button("进入主界面") {// 这将自动更新 UserDefaults 中的 "isLogin" 键isLogin = true}}}
}
3.首页退出登录
struct MainView: View {@AppStorage("isLogin") var isLogin: Bool = falsevar body: some View {VStack {Text("主界面")Button("退出登录") {// 这将自动更新 UserDefaults 中的 "isLogin" 键isLogin = false}}}
}