このPartでは、@State
と@Binding
について解説していきたいと思います。
変数とは?
まず、@State
、@Biding
を説明する前に、変数というのをしっかりと理解しておかなければならないので、変数からしっかりと説明していきたいと思います。
変数というのは、var
がついている値のことです。イメージ的には値を入れる箱だと思ってください。
実は、前回、前々回のPartでも変数を書いています。DiceアプリではrandomNumber
という変数を宣言して出目を入れいていました。モーダル遷移を実装する時には、isShowThirdView
という変数を宣言して、遷移するかどうかの値を入れていました。
というように、変数はプログラミングにとって最も重要です。
もし変数がなければ、「ボタンを押したらText
の文字を変える」というプログラムは書けません。
じゃあ、ボタンを押したらText
の文字を変えるというプログラムを見てみましょう。
struct ContentView: View {
@State var str = "Hello, world!"
var body: some View {
VStack {
Text(str)
.padding()
Button {
str = "ハローワールド"
} label: {
Text("ボタン")
}
}
}
}
まず、この画面はテキストとボタンがあるだけの画面です。
Text
で、str
を表示しています。str
は"Hello, world!"
なので、画面には、「Hello, world!」と表示されます。
ボタンを押すと、str
に"ハローワールド"
という文字列を代入(上書き)しています。それにより、画面に表示されているテキストが「ハローワールド」に変わります。
というように、Text
の中身をstr
という変数にしているため、ボタンを押したらText
の文字を変えることができるのです。str
という変数がなかったらこのプログラムは実装できません。
図解すると以下のような感じです。
@Stateとは?
SwiftUI開発だと、@State
がついている変数が出てきます。
では、@State
をつけた変数は通常の変数と、異なる点が2点あります。
- 値が変わったら、Viewがリロードされます。
-
Struct
の中で値を変更できます。
この2つの仕様が追加されます。
1. 値が変わったらViewがリロードされる
@State
がついた変数をボタンなどで変更すると、Viewがもう一度読み込まれます。
以下の図解は、ボタンを押したときに読み込まれる処理の順番を表した図解です。
もし、@State
がついていなかったら、ボタンを押してもViewの再描画が行われないので、Text
の文字は変わりません。
2. Structの中で値を変更できる
Swift言語のルール上、struct
の中で変数の変更はできません。(使うことはできる)
@State
をついていない変数を書き換えようとすると、
Cannot assign to property: 'self' is immutable
というエラーが起きます。
@State
をつけると、このエラーは解消されます。
まとめると、@State
は、struct
の中で値を変更できるようにし、変更するたびに再描画されるための修飾子です。
@Bindingとは?
次に@Binding
について解説していきたいと思います。こちらは、プログラミング初心者にとっては、少し難しいと思うかもしれませんが、まずはここでは、そんなこともできるんだなぁ程度で覚えてください。
この入門講座では、繰り返し@Binding
を使っていくので、いずれ覚えられるかと思います。
@Binding
というのは、一言で言うと、View間でのデータの共有ができる修飾子です。
画面Aと画面Bがあるとすると、画面Bで画面Aの@State
を変更したいときに@Binding
を使います。
一番簡単な例は、モーダル表示の画面を閉じる処理なので、前回のPart7の画面遷移を復習しながら@Binding
を使ってモーダル表示の画面を閉じる処理を行なっていきましょう。
適当にPart8
とかでプロジェクトを作成してください。
@Bindingを使ってみる
-
画面Bを作る
Part8を選択して、command + n
①SwiftUI Viewを選択
②Nextをクリック
①Save as:にBView.swift
と入力
②Createをクリック
これで、BView
が作れました。
-
画面先の装飾
BView
のvar body
の中を以下のように変更しましょう。ただ画面をオレンジ色にしているだけです。ZStack { Color(.orange) .edgesIgnoringSafeArea(.all) Text("BView") .font(.largeTitle) }
変更する場所
-
モーダル遷移処理作成
ContentView
からBView
に遷移させたいので、ContentView
を以下のように変更してください。struct ContentView: View { @State var isShowBView = false var body: some View { Button { isShowBView = true } label: { Text("BViewへ") .font(.largeTitle) } .sheet(isPresented: $isShowBView) { BView() } } }
変更する場所
実行して確認してみると、ボタンを押したらBViewにモーダル遷移するはずです。
-
閉じるボタンを作る
現状、下にスワイプすると画面を閉じれますが、ボタンでも閉じれるようにしましょう。
まずはボタンを作成します。
BView.swift
を開いてZStack
の中を以下のようにVStack
でText
を囲ってButton
を追加しましょう。ZStack { Color(.orange) .edgesIgnoringSafeArea(.all) VStack { Text("BView") .font(.largeTitle) Button { } label: { Text("閉じる") .font(.largeTitle) .padding() .background(.green) .foregroundColor(.white) .cornerRadius(10) } } }
追記する場所
これで、ボタンができました。
-
@Bindingを宣言する
画面を閉じるには、BView
を表示した処理の逆のことをしましょう。つまり、isShowBView
をfalse
にするということです。
ですが、isShowBView
はContentView
で宣言しているので、BView
では使えません。
そこで、@Binding
を使います。
以下のコードをBViewの中で宣言してください。@Binding var isShowBView: Bool
追記する場所
-
プレビュー削除
上記のように@Binding
を追加すると、プレビューを表示させるコードのところでエラーになります。
今回はプレビューはもう必要ないので削除しましょう。
-
@Bindingに値を渡す
command + bを押すと、プロジェクトでエラーが発生しているかどうか確認できます。
現状、ContentView
にエラーが発生しています。
ContentView.swift
を確認してください。
理由としては、BView
で宣言した@Binding
に値を渡していないからです。
なので、@Binding
に値を渡しましょう。
①丸ぽちをクリック
②Fixをクリック
そうすると、以下のように自動でBView
で宣言した@Binding
が表示されます。
ここにContentView
のisShowBView
を渡してあげます。
以下のように記述してください。BView(isShowBView: $isShowBView)
追記する場所
-
閉じる処理追加
では、最後に閉じる処理を追記しましょう。
ContentView
のisShowBView
をBView
に渡すことができたので、BView
でもisShowBView
が使えるようになりました。
BView
のButton
の中に以下のように処理を追記しましょう。Button { isShowBView = false } label: {
追記する場所
これでボタンを押したら画面が閉じるはずです。実行して確認してみましょう。
これが
@Binding
の使い方です。
他のViewの値をいじりたい時は、このように@Binding
を使いましょう。この後のPartでも@Binding
を使っていくので、何回も何回も使って覚えていきましょう。
全てのコード
全てのコード
import SwiftUI
struct ContentView: View {
@State var isShowBView = false
var body: some View {
Button {
isShowBView = true
} label: {
Text("BViewへ")
.font(.largeTitle)
}
.sheet(isPresented: $isShowBView) {
BView(isShowBView: $isShowBView)
}
}
}
import SwiftUI
struct BView: View {
@Binding var isShowBView: Bool
var body: some View {
ZStack {
Color(.orange)
.edgesIgnoringSafeArea(.all)
VStack {
Text("BView")
.font(.largeTitle)
Button {
isShowBView = false
} label: {
Text("閉じる")
.font(.largeTitle)
.padding()
.background(.green)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
}
}