【SwiftUI入門講座Part8】@State、@Bindingを理解しよう。~簡単なアプリを作って使い方を覚えよう~

作成中です。

Part8では、@Stateと@Bindingを解説していきたいと思います。

変数とは?

まず、@State、@Bindingを説明する前に、変数というの理解しておかないといけないので、変数から解説していきます。

変数というのは、varがついている値のことです。イメージ的には値を入れる箱だと思ってください。

実は、前回、前々回のPartでも変数を書いています。Diceアプリでは、randomNumberという変数を宣言して出目を入れていました。モーダル遷移を実装するときにはisShowThirdViewという変数を宣言して、遷移するかどうかの値を入れていました。

というように、変数はプログラミングにとってとても重要です。

もし、変数がなければ、「ボタンを押したらTextの文字を変える」というプログラムは書けません。

じゃあ、「ボタンを押したらTextの文字を変える」というプログラムを見てみましょう。

struct ContentView: View {
    @State var str = "Hello, world!"
    
    var body: some View {
        VStack {
            Text(str)
                .padding()
            Button(action: {
                str = "ハローワールド"
            }) {
                Text("ボタン")
            }
        }
        
    }
}

この画面を表示すると、テキストと、ボタンが表示されます。

Textでstrを表示しています。strは、"Hello, world!"なので、画面には「Hello, world」と表示されます。

ボタンを押すと、str"ハローワールド"という文字列を代入(上書き)しています。それにより、画面に表示されているテキストが、「”ハローワールド”」に変わります。

というように、Textの中身をstrという変数にしているため、このようにボタンを押したら、Textの文字を変えることができるのです。strがなかったら実装できません。

図解するとこんな感じです。

@Stateとは?

SwiftUI開発での、変数には基本的に@Stateがついていると思います。

じゃあ、@Stateをつけるとどうなるのかというと、

  1. 値が変わったら、Viewがリロードされます。
  2. Structの中で値を変更することができます。

この2つの仕様が追加されます。

1. 値が変わったらViewがリロードされる

@Stateがついた変数をボタンなどで変更すると、Viewがもう一度読み込まれます。

以下の図解は、ボタンを押したときの読み込まれる処理の順番をあわらした図解です。

もし、@Stateがついていなかったら、ボタンを押したら、Viewの再描画は行われないので、Textの文字は変わりません。

2. Structの中で値を変更することができる

Swift言語のルール上、Structの中で変数の変更はできません。(使うことはできる)

Cannot assign to property: 'self' is immutableと怒られてしまいます。

@Stateをつけると、このエラーは解消されます。

まとめると、@Stateは、structの中でプロパティを変更できるようし、変更するたびに再描画させるための修飾子です。

@Bindingとは?

次に@Bindingについて解説していきたいと思います。こちらは、プログラミング初心者にとっては、難しいと思うかも知れませんが、まずは、そんなこともできるんだなぁ程度で覚えてください。

@Bindingというのは一言で言うと、View間でのデータの共有ができる修飾子です。

画面Aと画面Bがあるとすると、画面Bで画面Aの@Stateを変更したいと言うときに、@Bindingを使います。

一番簡単な例は、モーダル表示の画面を閉じる処理なので、前回のPart画面遷移を復習しながら、@Bindingを使ってモーダル表示の画面を閉じる処理をやっていきましょう。

適当にPart8とかでプロジェクトを作ってください。

STEP.1
画面Bを作る

Part8を選択してcommand + n


①SwiftUI Viewを選択
②Nextをクリック


①Save as:にNextView.swiftと入力
②Createをクリック

これでNextViewが作れました。

STEP.2
画面先の装飾

NextViewを以下のように変更しましょう。ただ画面をオレンジ色にしただけです。

struct NextView: View {
    var body: some View {
        ZStack {
            Color(.orange)
            Text("NextView")
        }
    }
}

STEP.3
モーダル遷移させる

ContentViewを以下のように変更してください。

struct ContentView: View {
    @State var isShowNextView = false
    
    var body: some View {
        Button(action: {
            isShowNextView = true
        }) {
            Text("NextViewへ")
        }
        .sheet(isPresented: $isShowNextView) {
            NextView()
        }
    }
}

実行してみると、このように画面遷移できるはずです。

コード解説

Part7で解説しましたが、@Stateの解説をしたので、前回より深く理解できると思います。

11行目で、isShowNextViewというBool型(trueかfalseか)の@Stateの変数を持ちます。

19行目の、.sheet(isPresented: $isShowNextView)で、もしisShowNextViewtrueだったらNextView()に遷移させるというコードを書いています。

最初に画面が表示されたときは、isShowNextViewfalseのため、20行目は実行されません。15行目で@StateのisShowNextViewtrueにすると、再描画され、20行目が実行されます。という仕組みになっています。

STEP.4
閉じるボタンを作る

現状、下にスワイプすると画面を戻れますがボタンでも戻れるようにしましょう。まずはボタンを作ります。

NextView.swiftに、以下のように、VStackでTextを囲ってButtonを追記しましょう。

struct NextView: View {
    var body: some View {
        ZStack {
            Color(.orange)
            VStack {
                Text("NextView")
                Button(action: {
                    print("ボタンが押されたよ")
                }) {
                    Text("ボタン")
                }
            }
        }
    }
}

STEP.5
@Bindingを宣言する

画面を閉じるには、画面遷移と逆のことをしましょう。要は、isShowNextViewfalseにすると言うことです。ですが、isShowNextViewはContentViewで宣言されているので、NextViewでは使えません。

以下のようにCannot find 'isShowNextView' in scopeと言うエラーになります。

では、NextViewでも使えるようにしましょう。そこで@Bindingを使います。

NextViewに以下の変数を追加しましょう。

@Binding var isShowNextView: Bool

STEP.6
プレビュー削除

このようにやると、プレビューを表示させるコードでエラーになります。今回はプレビューはもう必要ないので削除しましょう。

STEP.7
@Bindingに値を渡す

command + bを押すとエラーがあるかどうかをチェックできます。現時点だと、ContentViewに以下のようにエラーが出ているはずです。

理由としては、NextViewで宣言した、@Bindingに値を渡していないからです。

①丸ぽちを押す
②Fixをクリック

そうすると以下のように、自動でNextViewで宣言した@Bindingが表示されます。

ここにContentViewのisShowNextViewを渡してあげます。

以下のように$をつけて記載してください。

NextView(isShowNextView: $isShowNextView)

コード解説
NextView(isShowNextView: $isShowNextView)

変数名が同じなのでわかりづらいですが、NextViewのisShowNextViewに、ContentViewのisShowNextViewを入れています。

STEP.8
閉じる処理

閉じる処理を入れましょう。ContentViewのisShowNextViewを渡すことができたので、NextViewでも使えるはずです。

以下のように、追記しましょう。

Button(action: {
    print("ボタンが押されたよ")
    isShowNextView = false
}) {

これで、ボタンを押すと、閉じれるようになってるはずです。

これが、@Bindingの使い方です。他のViewの値をいじりたいときは、このように@Bindingを使います。次のPartでも@Bindingを使っていくので、何回も使って覚えていきましょう。