SwiftUI @Environment 環境變數教學

當你的 App 層級很深時,如果要把資料從最上層傳到最下層(例如:使用者設定、色彩主題、登入狀態),一層一層透過 @Binding 傳遞會非常痛苦且難以維護。這時候就是 Environment (環境) 登場的時候了。

@EnvironmentObject (自定義環境資料)

這相當於 SwiftUI 的「依賴注入 (Dependency Injection)」。你可以在最上層注入一個資料物件,底下所有子視圖(無論多深)都可以直接存取它。

步驟 1: 注入 (Inject)

在父視圖使用 .environmentObject() 修飾符。

@StateObject var userSettings = UserSettings()

var body: some View {
    ContentView()
        .environmentObject(userSettings) // 注入環境
}

步驟 2: 讀取 (Read)

在任意深度的子視圖中,宣告 @EnvironmentObject 即可直接使用,不需要初始化。

struct DeepChildView: View {
    @EnvironmentObject var settings: UserSettings // 自動從環境中尋找
    
    var body: some View {
        Text("目前主題: \(settings.theme)")
    }
}
如果在子視圖宣告了 @EnvironmentObject 但父視圖忘記注入,App 會在執行時崩潰 (Crash)。

@Environment (系統環境值)

除了我們自定義的物件,SwiftUI 提供了許多內建的環境值 (Environment Values),例如:亮/暗模式、系統語系、畫面尺寸類別等。

我們使用 @Environment 並透過 KeyPath 來讀取這些值。

struct MyView: View {
    @Environment(\.colorScheme) var colorScheme // 獲取目前是 Light 或 Dark mode
    @Environment(\.dismiss) var dismiss // 獲取關閉視圖的方法
    @Environment(\.openURL) var openURL // 獲取打開瀏覽器的方法
    
    var body: some View {
        if colorScheme == .dark {
            Text("暗黑模式")
        }
        
        Button("關閉") {
            dismiss()
        }
    }
}

Environment 機制極大地簡化了全域資料的共享與傳遞,是構建大型 App 不可或缺的工具。