SwiftUI を使い始めると必ず出てくるのが @State と @Binding。でも「struct なのに状態が残るのはなぜ?」「$マークの意味は?」と混乱しやすいポイントです。本記事では、初めて触る人でも理解できるように、概念・仕組み・具体例をわかりやすく解説します。
1. Viewはなぜ状態を持てない?
SwiftUI の View は struct です。struct は値型なので、再生成されると中身がリセットされます。つまり本来は「状態を保持する」ことができません。
struct MyStruct {
var count = 0
}
var a = MyStruct()
a.count += 1 // → 1
a = MyStruct() // → 0 にリセット
SwiftUI の View も同じで、再描画のたびに struct が作り直されます。これだとボタンを押してもカウントが保持できないはずです。
2. @State の役割
@State は「この View 内だけで使う、一時的な状態を保存する箱」です。SwiftUI が裏で専用ストレージを確保し、View が再生成されても値を再利用してくれる仕組みになっています。
例:
struct CounterView: View {
@State private var count = 0 // 状態の箱
var body: some View {
VStack {
Text("カウント: \(count)")
Button("増やす") { count += 1 }
}
}
}
countは struct の中に直接保存されるわけではなく、SwiftUI が外部にストレージを持ちます。- 値が変わると View が再描画され、UI が自動で更新されます。
3. @Binding の役割
@Binding は「他の View が持っている @State を参照するひも」です。自分自身は状態を持たず、親ビューの状態を借りて操作できます。
struct ChildView: View {
@Binding var count: Int // 親の状態を参照
var body: some View {
Button("子ビューで増やす") {
count += 1 // 親の count が増える
}
}
}
struct ParentView: View {
@State private var count = 0 // 親が状態を保持
var body: some View {
VStack {
Text("カウント: \(count)")
ChildView(count: $count) // ← $をつけてバインディングを渡す
}
}
}
- 親は
@Stateで状態を持ちます。 - 子は
@Bindingでその状態を参照します。 $countは「Binding(参照ひも)」を作って子に渡す記号です。
4. $(ドル記号)の意味
count→ 値そのもの(Int)$count→ Binding(参照ひも)
例:
TextField("入力", text: $name)
- 入力欄に文字を入れると
nameに即反映。 - 逆に
nameの変更も TextField に反映。 - 双方向の同期(双方向バインディング)が成立します。
5. 内部で何が起きているか
@State は実際には State<T> というラッパーです。概念的にはこう書けます:
@propertyWrapper
struct State<Value> {
init(wrappedValue: Value)
var wrappedValue: Value { get nonmutating set }
var projectedValue: Binding<Value> { get }
}
wrappedValue→ 実際の値(例: count)projectedValue($count) → その値を共有する Binding
つまり、View が struct として再生成されても、SwiftUI が裏で同じストレージを再利用するので状態は保持されます。
図で表すと:
最初の描画 → ストレージ[count=0]
ボタン押下 → ストレージ[count=1]
再描画 → 新しいViewが作られるが、同じストレージ[count=1]を再利用
6. Environmentとの違い
@State→ その View 内限定の状態@Binding→ 親からひもで状態を借りる@Environment→ アプリ全体や親ビューが提供する共通値を読む(例:colorScheme,modelContext)
7. まとめ
@State: View 内の一時的な状態を保持。値が変われば自動で UI 再描画。@Binding: 親の@Stateを参照する仕組み。子ビューから親の状態を操作できる。$:@Stateから Binding(参照ひも)を取り出す記号。- View は struct なので本来状態を持てないが、SwiftUI が裏でストレージを持つため状態が残る。
8. 実例(フォーム画面)
struct EntryView: View {
@State private var fact = ""
@State private var interpretation = ""
@State private var date = Date()
@State private var showErr = false
@State private var errMsg = ""
@State private var savedToast = false
var body: some View {
VStack {
TextField("事実", text: $fact)
TextField("解釈", text: $interpretation)
DatePicker("日付", selection: $date)
Button("保存") {
if fact.isEmpty {
errMsg = "事実が未入力です"
showErr = true
} else {
savedToast = true
}
}
}
.alert(errMsg, isPresented: $showErr) {}
}
}
fact,interpretation,date→ 入力フォームの内容を保持。showErr,errMsg→ エラー用。savedToast→ 保存完了時の通知用。
すべてが @State で管理され、UI と自動同期します。

コメント