SwiftUI における変数の扱い方
SwiftUIにおいて、View内で変数を扱おうとこれまで通りvar を使用して宣言し変数の値を変更しようとするとなんとコンパイルエラーとなってしまうではありませんか。
Cannot assign to property: 'self' is immutable
- プロパティに値を代入できません。selfはイミュータブル(不変)です。
Cannot use mutating member on immutable value: 'self' is immutable
- イミュータブルの値に値を代入できません。selfはイミュータブル(不変)です。
これは、なぜ?
これは、SwiftUIのUI部品はViewを継承したStruct(構造体)であるため通常の変数(インスタンス変数)を更新することや、他のViewへ渡すことができないからです。
今回は、SwiftUIにおける変数の扱い方について記載していきます。
本記事作成時の環境
- macOS: Sonoma 14.1.1
- Xcode 15
- Swift 5.9
SwiftUIで変数を扱うには
SwiftUIで変数を扱うには、プロパティラッパー(property wrappers)を使用します。StateやBindingなど多くのデータ管理タイプがプロパティラッパーとして提供されています。
たとえばStateを使用した例で例えると変数宣言に「@Stateをつける事でSwiftUIフレームワークがView内で使用される変数を管理しデータバインディングし参照して表示しているUIの表示を更新てくれます。
データバインディング
アプリのUIと、UIに表示されるデータの接続を確立し、データの値が変更されるとそのデータに接続されている要素に変更が自動的に反映される仕組みです。
データを書き換えた場合は表示が更新
propertyを更新 → TextLabelの表示更新
TextLabelでデータを更新 → propertyへ反映
プロパティラッパ(Property Wrappers)の種類
プロパティラッパ(Property Wrappers)には以下にあげるいくつか種類があります。
- State
- Binding
- StateObject
- ObservableObject
- ObservedObject
- Published
- Environment
- EnvironmentObjects
- Preferences
今回はStateとBindingについて掘り下げていきます。
@State
通常の変数は先頭に@Stateは宣言して使用すします。 その変数を変更すると値を使用しているViewを再描画してくれます。
struct ContentViewState: View {
@State private var isSound: Bool = true
var body: some View {
Button(action: {
isSound.toggle()
}) {
Image(systemName: isSound ? "speaker.wave.3.fill" : "speaker.slash.fill")
}
}
}
- シンプルな型を扱う場合に使用
- 構造体、文字列、整数などの値の型を保存する必要がある場合に使用する。
@Binding
@State宣言された変数を子Viewでも使用したい場合@Bindingを使用します。
@State宣言された変数を子Viewに渡し扱うことができます。その変数を変更した場合親Viewの要素も更新されます。
import SwiftUI
struct ContentViewState: View {
@State private var isSound: Bool = true
var body: some View {
VStack {
Text(isSound ? "On" : "Off")
SoundButton(isSound: $isSound)
}
.padding()
}
}
struct SoundButton: View {
@Binding var isSound: Bool
var body: some View {
Button(action: {
isSound.toggle()
}) {
Image(systemName: isSound ? "speaker.wave.3.fill" : "speaker.slash.fill")
.foregroundStyle(.tint)
.imageScale(.large)
}
}
}
このサンプルでは子ViewであるSoundButtonにisSound
を渡し、子View:SoundButton内でタップを検出しisSoundの値をtoggleしています。
スピーカーアイコンのボタンをタップすると子ViewのSoundButton内でisSoundの値のtoggleされます。
連動して親ViewでisSoundを参照して表示しているTextが”On" or "Off"が切り替わります。
ボタンをタップすると切り替わる
←→
このようにSwiftUIではプロパティラッパーを使用しデータバインディングを行ないます。
まとめ
- SwiftUIで変数を扱うにはプロパティラッパーを利用する
- プロパティラッパを使用することでデータバインディングもSwiftUIが管理してくれる
- プロパティラッパにはState,Bindingなどいくつかの種類がある