バッテラが如く

プログラミングしましょ!

【SwiftUI】@Stateの基本的な使い方 (PropertyWrapper)

はじめに

SwiftUIで@がついたものをPropertyWrapperというらしいですが、その1つに@Stateがあります。

今回は@Stateについて深ぼる記事になります。

環境

この記事の情報は次のバージョンで動作確認しています。

* MacOS Monterey (12.1)
* Xcode (13.3.1)

概要

  • View内の変数の値を変えるため
  • 変数の値が変わったことをViewに通知するため

基本的な使い方

@Stateを頭につけて、あとは普通に変数宣言するだけ

@State private var hoge: Bool 

@Stateつけないと変数の値を変えられない件

変数の更新をVIewに伝えられない問題もあるんですが、一番の問題はそこじゃないんです。

そもそも変数の値を変えるタイミングはButtonなどが持つクロージャ内になります。

Viewはstructであるため、クロージャの中だとself(自分自身)の変更をしようとすると下記エラーが起きます。

Cannot use mutating member on immutable value: 'self' is immutable

つまり 変数の中身を変えられない ということになります。

これが@Stateをつける一番の意味となります。

余談ですが、

Swiftの言語仕様として、上記エラーはクロージャじゃなくメソッドならmutatingをつければエラーを回避可、

そもそもclassであればクロージャ内で書き換えできるみたいです。

どちらにせよViewに変更を通知できない問題は残るので意味ないですが。。

なぜ@Stateをつけるとなぜ変えられるのか?

A:分かりません

これがまだ分かって分かっていません。とりあえずおまじないと思っておきます。

@Stateの変数を変更したら画面更新される

これが@Stateを使う上で一番肝心な部分になります。

@Stateの変数の値が変わると画面が更新されます。。。

うん。それは察してるとは思うけど内部的にどうなってるかですよね。

分かる範囲で検証してみました。

initが呼ばれてたのでView自体が再生成されているってことです。

子View側の@Stateの変数の値が変わった場合にどうなるのか?

@Stateの変数を引数の値で初期化する場合に起きる問題

Variable used before being initialized

条件は不明ですがこのエラーが出てしまうことがあります。

この場合初期化方法を変えるとうまくいきます。

struct SecondView: View {
    
    @State private var text: String
    
    init(str: String) {
        _text = State(initialValue: str)
    }
    
    var body: some View {
        VStack {
            TextField("Hoge", text: $text)
            
        }
    }
}

ポイントとしてはtext_textとアンダースコア(_)をつけて変数にアクセスします。

代入する際は State(initialValue: 値)の形をとります。

エラーが出るケースと出ないケースがあるので、エラーが出た場合はこれを試してみてください。

おわりに

最後まで見ていただきヘペトナス!

読者登録・Twitterのフォローもお願いします。