SwiftUIのProperty Wrapperをマスターする①〜値型につける〜

こんにちは。ママさんエンジニアのトモヨです。
Swift UIになってからよくつけるようになった@StateなどのProperty Wrapper(プロパティラッパー)の理解度を深めるために勉強しました。
皆様も書いてみるとわかりやすいので実際にコードを書きながら実践してみると面白いです。

実際に書いたコードはこちらです。

下記の記事を参考に学びました。とっても詳しく記載されていてわかりやすかったです。私の記事では気になった部分も含め解説していきます。

https://blog.personal-factory.com/2021/01/23/how-to-use-propertywrapper-in-swiftui/

①単純なプロパティ

まずParentView.swiftというクラスを生成し、自動で入ったテキスト"Hello, World!"を定義してみましょう。

struct ParentView: View {
    private let helloWorld = "Hello, World!"
    var body: some View {
        Text(helloWorld)
    }
}

この場合helloWorldはParentViewでしか使用されないのでprivateを。
helloWorldの中身の文字は変更しないのでletを使用しましょう。

②@Stateを使うプロパティ

  • データが値型

  • データを更新をする

  • データの発生源がView自身

の場合は@Stateを使います。画面にボタンがあり、ボタンを押すとカウントが増える画面を作成します。

struct ParentView: View {
    @State private var counter = 0
    
    var body: some View {
        Button(action: {
            counter += 1
        }, label: {
            Text("counter is \(counter)")
        })
    }
}

UIKit使用時には@Stateを付与しなくても問題ありませんでしたが、SwiftUIからはエラーが出るようです。
Left side of mutating operator isn't mutable: 'self' is immutable

@Being

  • 値型のデータ

  • データを更新する

  • データの発生源は親Viewなど外から渡される場合

に使用します。今回は子Viewに先ほどのconterを渡してみましょう

struct ParentView: View {
    @State private var counter = 0
    
    var body: some View {
        ChildView(counter: $counter)
    }
}

struct ChildView: View {
    @Binding var counter: Int
    var body: some View {
        Button(action: {
            counter += 1
        }, label: {
            Text("\(counter)")
                .font(.title)
        })
            .border(Color.red)
    }
}
ChildView

引き渡されました。ここで確認しておきたいことはChildViewで増やした数は親Viewで定義したcountと同値か?ということです。
確認してみたいので親viewから子viewに画面遷移させてみます。

struct ParentView: View {
    @State private var counter = 0
    
    var body: some View {
            NavigationView {
                VStack{
                // 押すと増えるボタン
                Button(action: {
                    counter += 1
                }, label: {
                    Text("\(counter)")
                        .font(.title)
                })
                               //遷移するボタン
                NavigationLink(destination: ChildView(counter: $counter)) {
                    Text("child Viewへ遷移")
                }
            }.navigationBarTitle("ParentView", displayMode: .inline)
            }
            
    }
}

struct ChildView: View {
    @Binding var counter: Int
    var body: some View {
        // 子viewにもボタンを置く
        Button(action: {
            counter += 1
        }, label: {
            Text("\(counter)")
                .font(.title)
        })
            .border(Color.red)
    }
}

まずParentViewで3回ボタンを押す

ParentView

ChildViewに遷移。3が引き継がれていることがわかります。

ChildView

ChildViewでカウントを増やし5にする

ChildView

ParentViewに戻ると5になっています。

ParentView

このことから値は同じものを見ていることがわかります。

@Environment

@State、@Bindingは自分で作ったデータを処理する場合でしたが、
@EnvironmentはViewの環境値を読み取れるProperty Wrapper

こちらで読み込める値が調べられます。

struct EnvironmentSample: View {
    @Environment(\.colorScheme) var colorScheme: ColorScheme
    var body: some View {
        if colorScheme == .dark {
            Text("dark mode").foregroundColor(.red)
        } else if colorScheme == .light {
            Text("light mode").foregroundColor(.red)
        } else {
            Text("").foregroundColor(.red)
        }
    }
}

ここまで、値に付随するProperty Wrapperを学習してきました。次回はクラスに付随するProperty Wrapperについて学習します。

この記事が気に入ったらサポートをしてみませんか?