SwiftUI笔记之@Environment和@EnvironmentObject的用法和区别

117 阅读2分钟

1.@Environment

  • 用途:访问系统或框架预定义的环境值(如设备方向、颜色方案、布局方向等)。
  • 特点
    • 用于读取系统提供的全局配置信息。
    • 环境值是预定义的(例如 .colorScheme.locale.managedObjectContext)。
    • 可以直接通过 @Environment 属性包装器访问,无需手动注入。
  • 示例
struct ContentView: View {
    // 读取系统环境值
    @Environment(\.colorScheme) var colorScheme //颜色模式 (暗黑/明亮)
    @Environment(\.locale) var locale // 本地化设置
    @Environment(\.horizontalSizeClass) var sizeClass // 屏幕尺寸

    var body: some View {
        Text(colorScheme == .dark ? "Dark Mode" : "Light Mode")
    }
}

2.自定义环境值

  • 自定义环境值的 Key
// 定义环境值的 Key
struct ThemeColorKey: EnvironmentKey {
    // 默认值(未注入时的值)
    static let defaultValue: Color = .blue
}
  • 扩展 EnvironmentValues
extension EnvironmentValues {
    // 添加名为 `themeColor` 的自定义环境值
    var themeColor: Color {
        get { self[ThemeColorKey.self] }
        set { self[ThemeColorKey.self] = newValue }
    }
}
  • 注入自定义环境值
    在父视图中,使用 .environment 修饰符注入自定义值:
struct ParentView: View {
    var body: some View {
        ChildView()
            // 注入自定义环境值(覆盖默认的 blue)
            .environment(\.themeColor, .red)
    }
}
  • 在子视图中使用自定义环境值
struct ChildView: View {
    // 读取自定义环境值
    @Environment(\.themeColor) var themeColor

    var body: some View {
        Text("Hello SwiftUI")
            .foregroundColor(themeColor) // 使用环境值
    }
}

3.@EnvironmentObject

  • 用途:注入自定义的可观察对象ObservableObject),用于在视图层级中共享数据。
  • 特点
    • 用于传递自定义的全局数据(如用户信息、应用状态等)。
    • 需要手动通过 .environmentObject() 方法注入到视图层级。
    • 依赖对象的类型(而不是预定义的 KeyPath)。
    • 如果未正确注入对象,应用会崩溃(需确保对象存在)。
  • 示例
// 自定义数据模型
class UserSettings: ObservableObject {
    @Published var isLoggedIn = false
}

// 注入对象到根视图
@main
struct MyApp: App {
    let settings = UserSettings()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(settings)
        }
    }
}

// 子视图中使用
struct ContentView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Text(settings.isLoggedIn ? "已登录" : "未登录")
    }
}