MENU

SwiftUIの@Stateを完全マスター!状態管理の基礎から実践まで

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 など、他の状態管理について詳しく解説する予定です。お楽しみに!

プログラミングの独学におすすめ
プログラミング言語の人気オンラインコース
独学でプログラミングを学習している方で、エラーなどが発生して効率よく勉強ができないと悩む方は多いはず。Udemyは、プロの講師が動画で実際のプログラムを動かしながら教えてくれるオンライン講座です。講座の価格は、セール期間中には専門書籍を1冊買うよりも安く済むことが多いです。新しく学びたいプログラミング言語がある方は、ぜひUdemyでオンライン講座を探してみてください。
目次