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