【SwiftUI】EnvironmentValueを追加する

SwiftUI

SwiftUIではEnvironmentValuesが多数用意されており、Viewから@Environmentで参照する事が出来ます。

システムで設定された値を読み取ったり、設定した値が子Viewで読み取る事ができ非常に便利です。

今回はこのEnvironmentValueに独自のものを追加する方法を紹介します。
Read Onlyになる基本形と、変更可能な形式の2種を紹介します。

EnvironmentValueの追加(Read Only)

まずはRead Onlyの基本形です。

struct ReadOnlyKey:EnvironmentKey {
    static let defaultValue = "ReadOnlyValue"
}

extension EnvironmentValues {
    var readOnlyValue: String {
        get{ self[ReadOnlyKey.self] }
        set{ self[ReadOnlyKey.self] = newValue }
    }
}

Keyの構造体を定義しEnvironmentValuesにプロパティとして追加します。
追加するコード自体はこれだけです。

実際に使って確認してみましょう。

struct ContentView: View {
    
    @Environment(\.readOnlyValue) var readOnlyValue
    
    var body: some View {
        Text(readOnlyValue)
            .padding()
    }
}

既存のEnvironmentValueと同じですね。
@Environmentで指定するのは、Key構造体の名前ではなくEnvironmentValuesに追加したプロパティ名である事に注意してください。

親Viewから値を指定して渡す時は以下の様になります。

@main
struct EnvironmentValueTestApp: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.readOnlyValue, "TestValue")
        }
    }
}

これも既存のものと同じですね。

非常に簡単に実装できますので、子Viewへ値を渡したい時に非常に便利なので是非活用してみてください。

値が変更できるEnvironmentValue

EnvironmentKeyにBindingを使います。
正確に言うとEnvironmentValueの値は変わっていませんが、
おおよそEnvironmentValueの値を変えると言われてイメージするものだと思います。

struct BindingKey:EnvironmentKey {
    static let defaultValue:Binding<String> = .constant("BindingValue")
}

extension EnvironmentValues {
    var bindingValue:Binding<String> {
        get{ self[BindingKey.self] }
        set{ self[BindingKey.self] = newValue }
    }
}

Bindingを使うので中身の値を変更する事が出来ます。
また、Bindingなので変更に応じたViewの再描画が起こります。

使う時にはいくつか注意が必要です。
まず1つ目は親Viewで@Stateでマークした変数をenvironmentで渡す必要があります。

@main
struct EnvironmentValueTestApp: App {
    
    @State var stateValue = "StateValue"
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.bindingValue, $stateValue)
        }
    }
}

もう1つは、あくまで@BindingでなくBinding<>だと言うことです。
値を扱うには変数名.wrappedValueが必要です。
EditModeのようなイメージですね。

struct ContentView: View {
    
    @Environment(\.bindingValue) var bindingValue
    
    var body: some View {
        VStack {
            Text(bindingValue.wrappedValue)
                .padding()
            
            Button("Change"){
                bindingValue.wrappedValue = "Test"
            }
        }
    }
}

@Bindingで使いたい場合は@Environmentの後に書きます。

struct ContentView: View {
    
    @Environment(\.bindingValue) @Binding var bindingValue
    
    var body: some View {
        VStack {
            Text(bindingValue)
                .padding()
            
            Button("Change"){
                bindingValue = "Test"
            }
        }
    }
}

これで中身の値が変更できるEnvironmentValueができました。
SwiftUI標準で用意されているEnvironmentValueに近い扱いが出来るので、
非常に便利でありコードの統一感もでて読みやすくなると思います。

コメント

タイトルとURLをコピーしました