「 変数の更新と同時に画面の状態も変化させたい… 」
「 変数の値の更新ができないんだけど、どうすればいい…? 」
そんなお悩みを解決するのがSwiftUIが提供する機能「@State」です!
SwiftUIでは、データとViewの状態共有に”データバインディング”という仕組みが用いられています。例えば、
[ユーザーが”おはよう”と文字を打つ] → [画面に”おはよう”と文字が表示される]
といった動きにもデータとViewの状態共有が必要なわけですね。
@Stateはこのデータバインディングを実装するための機能の一つであり、SwiftUIでのさまざまな機能実装において非常に重要な役割を持ちます。
今回はそんな「@State」の機能解説と、基本的な使い方をお話します。
[ 本記事はこんな人におすすめ ]
・SwiftUIをこれから学ぶ
・@Stateの使い方について知りたい
・データバインディングの仕組みを知りたい
環境・バージョン
> Swift 5.7.2
> Xcode 14.2
> macOS 12.5 Monterey
@StateはSwiftUIの大きな特徴であるデータバインディングという仕組みの1つです。
データとViewを紐付けることでデータの更新時に自動的にViewも更新されるという連動的な仕組みがデータバインディングの特徴です。
また、@Stateが付与されたプロパティは”状態変数“とも呼ばれます。
データバインディングとは?
データバインディング(data binding)は、プログラミングにおいて、あるオブジェクトのプロパティ(変数)に別のオブジェクトの値を自動的に関連づける仕組みのことを指します。
例えば、アプリケーションのシステム設定で自身の名前を変更したら、アプリ内で表示されている各箇所の名前も連動して更新されますね。データモデルの値が変更されることで、関連づけられている値も自動的に反映されるため、プログラマが手動で値を更新する必要がありません。これがデータバインディングの動きです。
データバインディングは、アプリケーションの開発やメンテナンスを簡素化し、コードの可読性や再利用性を高めることができます。特に、大規模なアプリケーションにおいては、データバインディングを活用することで、開発者が手動でデータの同期をする必要がなくなるため、開発効率を大幅に向上させることができます。
状態変数には大きく以下2つの特性があります。
[ @Stateが付与されたプロパティの特徴 ]
それぞれの特徴を見ていきましょう。
@Stateが付与された変数は、保持する値の更新が可能になります。
SwiftUIのViewはstruct(構造体)によって構成されており、struct内のプロパティは
基本的に保持する値の変更ができません。
(mutatingキーワードを用いて値を更新する方法がありますが、今回は割愛します)
以下のように、body内の処理によってプロパティの値を更新しようとするとエラーが発生します。
発生するエラー :
Left side of mutating operator isn’t mutable: ‘self’ is immutable
“ミューティング演算子の左辺がミュータブル(可変)でない。
‘self’ はイミュータブル(不変)である”
この値の更新を可能にするために、SwiftUIでは「@State」を使用します。
プロパティを@Stateでラップすることで状態変数となり、保持する値の更新が可能になります。
値の更新が可能になったことで、上記のエラーも解消します。
@Stateが付与されたプロパティ(状態変数)は、SwiftUIの監視対象になります。
状態変数の値が変化すると、SwiftUIが更新を検知してViewを自動的に再描画します。
これにより、アプリ内でユーザーの操作によって画面の状態を変化させていく動きを表現することができます。
@Stateを用いたサンプルコードで”値の更新“と”Viewの再描画“を確認してみましょう。
numberの値がTextによって画面表示され、ボタンタップによってnumberの値がプラス1されるシンプルなアプリです。
struct ContentView: View {
@State var number = 0 // SwiftUIが値の更新を監視
var body: some View {
VStack {
Text("\(number)")
.font(.largeTitle)
Button("値をプラス") {
number += 1 // 値を更新
}
} // VStack
} // body
} // View
内部で起きている処理の流れは以下のようになっています。
処理の流れをイメージしながら画面の動きを見てみてください。
[アプリ内部処理の流れ]
画面上で値の変化を目で見ることができるのは、SwiftUIが値の更新があるたびにViewを再描画してくれているおかげということですね。
これがデータとViewを紐付けるデータバインディングの仕組みです。
“プロパティの値更新を可能にする”
“プロパティの状態をSwiftUIが監視(モニタリング)する”
これらの状態変数の特性を覚えておきましょう。
@Stateの概要が理解できたら、基本的な使い方を見ていきましょう。
前項目のサンプルでも使われていたようなプロパティの変更の監視を用いたView表現で@Stateはよく使われます。
例えば、String型の状態変数を用いたSwiftUIのView機能に「TextField」があります。TextFieldはテキスト入力機能をもち、ユーザーの文字入力を@Stateプロパティによって監視して画面表示します。
TextFieldの使い方について詳しくは、以下の記事をご参照ください。
struct ContentView: View {
@State var inputText: String = "" // ⬅︎入力文字が保持される状態変数
var body: some View {
VStack {
Text(inputText)
.font(.largeTitle)
TextField("文字を入力", text: $inputText) // ⬅︎ 状態変数と紐付ける
} // VStack
} // body
} // View
これもプロパティの変更を監視して、SwiftUIが画面を更新してくれている一つの例です。
以下のように、プロパティに何かしらの変更入力があるたびに、SwiftUIはViewを更新してプロパティの値と画面表示を同期します。
Bool値のプロパティを状態変数として宣言することで、”true“と”false“の状態切り替えに紐付けて2パターンのViewを切り替えるスイッチングのような実装ができます。
以下の例は、if文による条件分岐と@State変数の更新を組み合わせたサンプルコードです。
状態変数isCheckの更新によってif文の条件が再判定され、ベルアイコンのデザインがスイッチングします。
struct ContentView: View {
@State var isCheck: Bool = false // ⬅︎Bool値が保持される状態変数
var body: some View {
VStack(spacing: 20) {
if isCheck {
Image(systemName: "bell.fill") // trueの時の状態
.font(.largeTitle)
.foregroundColor(.black)
} else {
Image(systemName: "bell.slash")// falseの時の状態
.font(.largeTitle)
.foregroundColor(.gray)
}
Button("切り替えボタン") {
isCheck.toggle() // Bool型ブロパティの値を反転する
}
} // VStack
} // body
} // View
切り替えボタンのタップによって状態変数icCheckのBool値が反転し、値の更新に連動してベルマークの”種類”と”色”が変化していますね。
Bool変数の更新と紐付けるViewを増やせば、複数のViewを同時にスイッチングさせることもできます。
また、上記のようなif elseの記述は”三項(条件)演算子“を用いて書くこともできます。
@State var isCheck: Bool = false // Bool型の状態変数
// 三項(条件)演算子での記述
Image(systemName: isCheck ? "bell.fill" : "bell.slash") // ⬅︎
.font(.largeTitle)
.foregroundColor(isCheck ? .black : .gray) // ⬅︎
// ⬆︎同じ実装になる⬆︎
// ⬇︎同じ実装になる⬇︎
// if elseでの記述
if isCheck {
Image(systemName: "bell.fill")
.font(.largeTitle)
.foregroundColor(.black)
} else {
Image(systemName: "bell.slash")
.font(.largeTitle)
.foregroundColor(.gray)
}
◯三項(条件)演算子の記述法
・条件A ? a : b ▶︎ 条件Aがtrueならaの処理、
条件Aがfalseならbの処理
⬇︎条件が増えると可読性❌⬇︎
・条件A ? a : 条件B ? b : c ▶︎ 条件Aがtrueならaの処理
条件Aがfalse,条件Bがtrueならbの処理
条件A, 条件Bどちらもfalseならcの処理
※三項( 条件)演算子を使う際に注意すること
三項(条件)演算子で記述することによってif文をコンパクトにまとめて記述することができますが、条件項目が多かったり条件が複雑だと可読性が悪くなってしまう点に注意が必要です。
単純な2パターンの返り値を分岐させて取得したいなど、簡易的な条件分岐の場合には活用を検討してみるなど、適切にif文との使い分けをするのが望ましいです。
if文と同様に三項演算子も各種の変数の型(String, Int, etc…)と演算子による条件指定ができます。
if文と演算子については下記の記事にも書いているので、ぜひご覧ください。
以上、プロパティラッパーの一種である@Stateについてでした!
要点をおさらいしておきましょう。
・プロパティの値の更新が可能になる
・SwiftUIが値の更新を監視(モニタリング)する
・値の更新を検知すると、SwiftUIがViewを再描画する
SwiftUIの多くの機能で状態変数は使われています。扱い方を理解しておくことで開発に役立つこと間違いなしです
\ SHARE /
アプリ開発が学べる勉強会を開催中!
CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。
徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!
本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!
まずは、現役エンジニアに相談できる無料相談をご利用ください。
2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。