SwiftUIでアプリ開発を始めると、必ず出会うのが @State
です。「なんとなく使っているけど、実際どういう仕組み?」という方も多いのではないでしょうか。
この記事では、@State
の基本概念から実践的な使い方まで、分かりやすく解説していきます。
目次
@Stateとは何か?
@State
は、SwiftUIのプロパティラッパーの一つで、ビューの状態(データ)を管理するために使用します。
最も重要な特徴は、@State
で管理された値が変更されると、自動的にビューが再描画されることです。
struct SimpleCounterView: View {
@State private var count = 0 // この値が変わると画面も更新される
var body: some View {
VStack {
Text("カウント: \(count)")
Button("増やす") {
count += 1 // ここで値を変更すると画面が自動更新
}
}
}
}
なぜ@Stateが必要なのか?
通常のSwiftでは、構造体のプロパティは変更できません(mutating
を使わない限り)。しかし、SwiftUIのビューは構造体として定義されています。
// これはエラーになる
struct BadExampleView: View {
var count = 0 // 通常のプロパティ
var body: some View {
Button("増やす") {
count += 1 // エラー:Cannot assign to property
}
}
}
// @Stateを使えば解決
struct GoodExampleView: View {
@State private var count = 0 // @Stateプロパティ
var body: some View {
Button("増やす") {
count += 1 // OK!
}
}
}
@State
は、この制限を回避し、さらにビューの自動更新も提供してくれる優れた仕組みです。
基本的な使い方
1. Bool値での切り替え
struct ToggleExampleView: View {
@State private var isOn = false
var body: some View {
VStack {
Text(isOn ? "オン" : "オフ")
Toggle("スイッチ", isOn: $isOn) // $マークに注目
}
}
}
2. 文字列の入力
struct TextInputView: View {
@State private var inputText = ""
var body: some View {
VStack {
TextField("何か入力してください", text: $inputText)
Text("入力内容: \(inputText)")
}
}
}
3. 配列の管理
struct TodoListView: View {
@State private var todos = ["買い物", "掃除"]
@State private var newTodo = ""
var body: some View {
VStack {
List(todos, id: \.self) { todo in
Text(todo)
}
HStack {
TextField("新しいタスク", text: $newTodo)
Button("追加") {
if !newTodo.isEmpty {
todos.append(newTodo)
newTodo = ""
}
}
}
}
}
}
$マーク(バインディング)の重要性
@State
を使うとき、しばしば $
マークを見かけます。これはバインディングと呼ばれる仕組みです。
@State private var text = ""
// 値そのものを取得
Text(text) // $なし
// バインディング(双方向の接続)を作成
TextField("入力", text: $text) // $あり
text
:値そのものを参照$text
:値との双方向の接続を作成(TextField が値を変更できる)
実践的な例:簡単な計算機
実際のアプリでどのように使うか、簡単な計算機を作ってみましょう。
struct CalculatorView: View {
@State private var firstNumber = ""
@State private var secondNumber = ""
@State private var result = 0.0
@State private var operation = "+"
var body: some View {
VStack(spacing: 20) {
Text("簡単計算機")
.font(.title)
HStack {
TextField("数値1", text: $firstNumber)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.decimalPad)
Picker("演算", selection: $operation) {
Text("+").tag("+")
Text("-").tag("-")
Text("×").tag("*")
Text("÷").tag("/")
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 120)
TextField("数値2", text: $secondNumber)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.decimalPad)
}
Button("計算") {
calculate()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
Text("結果: \(result, specifier: "%.2f")")
.font(.title2)
}
.padding()
}
func calculate() {
guard let first = Double(firstNumber),
let second = Double(secondNumber) else { return }
switch operation {
case "+":
result = first + second
case "-":
result = first - second
case "*":
result = first * second
case "/":
result = second != 0 ? first / second : 0
default:
result = 0
}
}
}
@Stateを使う際のベストプラクティス
1. privateを必ず付ける
@State private var count = 0 // Good
@State var count = 0 // 推奨されない
2. 適切な初期値を設定
@State private var text = "" // 空文字で初期化
@State private var isLoading = false // falseで初期化
@State private var items: [String] = [] // 空配列で初期化
3. 複雑なデータ構造は他の状態管理を検討
// 単純な値は@State
@State private var userName = ""
// 複雑なオブジェクトは@ObservedObjectや@StateObjectを検討
@StateObject private var viewModel = UserViewModel()
よくある間違いとその対処法
間違い1: @Stateなしで値を変更しようとする
// エラーになる
struct BadView: View {
var count = 0
var body: some View {
Button("増やす") {
count += 1 // エラー
}
}
}
間違い2: $を付け忘れる
// バインディングが必要な場所で$を忘れる
TextField("入力", text: inputText) // エラー
// 正しい書き方
TextField("入力", text: $inputText)
まとめ
@State
は SwiftUI の状態管理の基本中の基本です。重要なポイントをまとめると:
- 値が変更されると自動でビューが更新される
- 必ず
private
を付ける - バインディング(
$
)を理解する - 単純な値型に使用する
@State
をマスターすることで、インタラクティブなSwiftUIアプリを作れるようになります。まずは簡単な例から始めて、徐々に複雑なアプリに挑戦してみてください!
次回は @Binding
や @ObservedObject
など、他の状態管理について詳しく解説する予定です。お楽しみに!