无需在 SwiftUI 的 View 中声明 @Environment 属性即可访问环境值

问题描述 投票:0回答:1

我正在尝试访问我的自定义环境值,而不在

@Environment
中声明
View
属性。那么为什么我需要它?

让我们看一下例子。我有我的自定义环境值:

enum Theme {
    case black
    case white

    func color() -> Color {
        // ... Get color functional
    }
}

struct ThemeKey: EnvironmentKey {
    static let defaultValue: Theme = .black
}

extension EnvironmentValues {
    var theme: Theme {
        get { self[ThemeKey.self] }
        set { self[ThemeKey.self] = newValue }
    }
}

要使用它,我必须在我的

@Environment
中写入
View
属性,如下所示:

struct AppView: View {
    var body: some View {
        ContentView()
            .environment(\.theme, .white)
        }
    }
}

struct ContentView: View {
    @Environment (\.theme) private var theme

    var body: some View {
        Rectangle()
            .fill(theme.color())
    }
}

那么如果我想在我的所有视图中访问

theme
环境值怎么办?例如,我可以编写
View
的扩展,它会像这样得到
theme

extension View {
    var theme: Theme {
        return Environment(\.theme).wrappedValue
    }
}

struct ContentView: View {
    var body: some View {
        Rectangle()
            .fill(theme.color())
    }
}

但它不起作用,Xcode 显示错误

Accessing Environment<Theme>'s value outside of being installed on a View. This will always read the default value and will not update.

所以我的问题是我是否必须在视图中添加

@Environment
属性才能访问
theme
值,或者我可以通过其他方式来实现,例如不声明
@Environment
属性?

ios swift swiftui
1个回答
0
投票

您遇到的错误消息源于对 SwiftUI 环境系统设计工作方式的根本误解。

@Environment
属性包装器专门设计用于将环境值注入到 SwiftUI 视图中。此机制可确保您的视图在环境值发生变化时自动更新,遵循 SwiftUI 的响应式设计原则。尝试在不使用
@Environment
(或其他适当的机制)的情况下直接访问环境值会绕过此系统,从而导致您所观察到的问题。

当您尝试在

View
上的扩展中创建计算属性以直接访问环境值时,您将绕过环境的预期使用模式。 SwiftUI 的环境不仅仅是一个可以随时访问值的简单字典;它还包括一个简单的字典。它是一个托管系统,可沿视图层次结构传播值,确保有效处理更新。

但是,如果您的目标是减少样板代码并使主题值在视图中轻松访问,而无需每次都显式声明

@Environment
属性,那么您有几种选择:

1.使用辅助视图

您可以创建一个小助手

View
,将环境值注入到其子视图中。这种方法允许您抽象出环境访问:

struct WithTheme<Content: View>: View {
    @Environment(\.theme) var theme
    let content: (Theme) -> Content

    init(@ViewBuilder content: @escaping (Theme) -> Content) {
        self.content = content
    }

    var body: some View {
        content(theme)
    }
}

// Usage
struct ContentView: View {
    var body: some View {
        WithTheme { theme in
            Rectangle()
                .fill(theme.color())
        }
    }
}

2.视图修改器

另一种方法是创建一个自定义视图修饰符,将主题注入视图中。这更符合SwiftUI的设计理念,可以让你灵活地使用环境值:

struct ThemeModifier: ViewModifier {
    @Environment(\.theme) var theme

    func body(content: Content) -> some View {
        content
            .fill(theme.color()) // Assuming the content can be filled with a color
    }
}

extension View {
    func withTheme() -> some View {
        modifier(ThemeModifier())
    }
}

// Usage
struct ContentView: View {
    var body: some View {
        Rectangle()
            .withTheme()
    }
}

在这两种方法中,您都通过利用

@Environment
属性包装器来访问环境值来遵循 SwiftUI 的预期设计。这可确保您的视图保持响应状态,并在环境值发生变化时自动更新。

SwiftUI 不支持直接访问这些模式之外的环境,并且正如您所发现的,这会导致错误或意外行为。关键是在框架的指导方针内工作以实现所需的功能。

© www.soinside.com 2019 - 2024. All rights reserved.