@State — SwiftUIでの目的と使い方

著者: IT Sectr 公開日: 2026-06-19 読了時間: 7 分

@Stateは、1つのビュー内でローカルステートを管理するためのSwiftUIのProperty Wrapperです。@Stateプロパティが変更されるたびに、SwiftUIは自動的にビューを再描画し、手動の更新呼び出しなしでインターフェースをリアクティブにします。Apple Developer Documentation (2025)によると、@Stateは1つのビューに属する単純な型や構造体に推奨されます。@Stateは、SwiftUIインターフェースにインタラクティビティを追加する最も簡単な方法です。

重要なポイント

  • @State — 1つのビューに属するローカルステート用のProperty Wrapper
  • 自動更新 — @Stateプロパティが変更されるとSwiftUIがbodyを再実行
  • 単純な型 — @StateはString、Int、Bool、enum、structで機能
  • 子ビューに渡さない — 子コンポーネントからの変更には@Bindingを使用
  • private — @Stateプロパティは常にprivate修飾子で宣言

SwiftUIの@Stateとは?

@StateはSwiftUIに組み込まれたProperty Wrapperで、ビューが自身のステートを保存および追跡できるようにします。@Stateの値が変更されると、SwiftUIはbodyプロパティを再実行してビューを自動的に再描画します。これがSwiftUIにおけるリアクティブプログラミングの基礎です。開発者はステートを宣言し、フレームワークがインターフェースの同期を処理します。

@Stateは、SwiftUIが管理するヒープ上にストレージ領域を作成します。この領域は永続的であり、レンダリングのたびに発生するビュー構造体の繰り返し初期化を乗り越えて存続します。SwiftUIは、ビューの識別子(階層内の位置から生成)を使用して、@Stateプロパティを特定のビューにバインドします。これにより、親ビューが更新されてもステートがリセットされません。

重要な制限:@Stateは値型(struct、enum、プリミティブ)のみを対象としています。参照型(クラス)の場合は、@StateObjectまたは@ObservedObjectを使用してください。@Stateプロパティにクラスを割り当てると、SwiftUIはオブジェクト内の変更を検出できません — 参照の完全な置き換えのみを検出します。

@Stateの内部動作

SwiftUIは@Stateを内部のStorageメカニズムを通じて実装しています。各@Stateプロパティは、ビューの特別なストレージコンテナに保存される専用のメモリセルを取得します。wrappedValueへの書き込みが発生すると、SwiftUIはdidSetを介して依存関係グラフに再描画の必要性を通知します。

swift
struct ContentView: View {
    @State private var name: String = "User"
    @State private var isLoggedIn: Bool = false

    var body: some View {
        VStack {
            Text("こんにちは、\(name)")
            Button(isLoggedIn ? "ログアウト" : "ログイン") {
                isLoggedIn.toggle()
            }
        }
    }
}

この例では、name(String)とisLoggedIn(Bool)の2つの@Stateプロパティがあります。isLoggedIn.toggle()が呼び出されると、SwiftUIはContentViewを更新が必要としてマークし、次のレンダリングサイクルでbodyを再実行します。重要なポイント:@Stateプロパティは常にprivate修飾子で宣言されます — これは、ステートが現在のビューに排他的に属し、外部から直接変更されるべきではないことを示します。

変更を監視するために、SwiftUIはCombineのCurrentValueSubjectを使用します。各@Stateプロパティは、変更のたびにシステムに通知する隠れたパブリッシャーを作成します。これにより、SwiftUIは必要最小限のビューセットのみを再描画し、階層全体の更新を回避できます。

プロジェクトで@Stateを使用するタイミング

@Stateは単純なローカルステートに最適です:検索のテキストフィールド、モーダルウィンドウのブールフラグ、設定のトグル、カウンター、選択されたリストアイテム。値が1つのビューとその子コンポーネント(@Binding経由)でのみ使用される場合、@Stateが正しい選択です。ビューが閉じられても存続すべきステート(フォームデータなど)の場合、ビューが階層に残っている限り@Stateも機能します。

  • テキストフィールド — TextFieldに入力されたテキストを保存する@State
  • ブールフラグ — モーダルウィンドウやシートの表示/非表示用の@State
  • 要素の選択 — 選択されたタブや行を追跡する@State
  • カウンター — 増加/減少のある数値用の@State
  • 中間計算 — ビュー内で結果をキャッシュする@State

@Stateをグローバルなアプリケーションステート、ネットワークデータのキャッシュ、または複数の画面で使用されるオブジェクトには使用しないでください。@StateObjectと@EnvironmentObjectがこれらの目的のために設計されています。また、@Stateは大量のデータの保存には適していません — 変更のたびにビュー全体が再描画されます。

@Stateと@Bindingの連携

@Bindingは、親ビューの@Stateとそのステートを変更する必要がある子ビューとの間の橋渡しです。親が@Stateを宣言し、子コンポーネントは$プロジェクションを介してBindingを受け取ります。子ビューでBindingを変更すると、親の@Stateが自動的に更新され、その逆も同様です。これにより、フィードバック機能を備えた単一方向のデータフローが確保されます。

swift
struct ParentView: View {
    @State private var text: String = ""

    var body: some View {
        ChildView(text: $text)
    }
}

struct ChildView: View {
    @Binding var text: String

    var body: some View {
        TextField("Enter text", text: $text)
    }
}

リストでは、ParentViewが@State textを所有し、ChildViewが$textをBindingとして受け取ります。ChildView内のTextFieldは、text: $textを介してこのBindingにバインドされます。ユーザーがTextFieldに入力すると、Bindingを介してChildViewで値が変更され、ParentViewの@Stateが更新されます。両方のビューが新しい値で再描画されます。

@State使用時の一般的な間違い

最も一般的な間違いは、@Stateプロパティにクラスを割り当てることです。@State var model = MyClass()と書くと、SwiftUIはクラス内のプロパティの変更を追跡できません — オブジェクトの置き換えのみを検出します。クラスの場合は、常に@StateObjectを使用してください。2番目の一般的な問題は、private修飾子なしで@Stateを宣言することで、ステートのカプセル化の原則に違反します。

$なしで@Stateを直接子ビューに渡すことも、もう1つの典型的な間違いです。TextField(text: $text)の代わりにTextField(text: text)を渡すと、子コンポーネントはバインディングではなく単なる文字列を受け取ります。TextFieldでのテキスト変更は親の@Stateと同期されません。Bindingを渡すには常に$プロジェクションを使用してください。

3番目の間違いは、関連データに対する複数の@Stateプロパティです。複数の値が論理的に1つの全体を形成する場合(フォームフィールドなど)、それらを1つの@Stateを持つ1つの構造体に結合してください。これにより、子ビューへのステートの受け渡しが簡素化され、個別の更新トリガーの数が減ります。

SwiftUIでの@State使用例

@Stateは、ほとんどのSwiftUIプロジェクトで基本的なインタラクティビティに使用されています。@Stateがテキストフィールドとローディングステートを管理するログインフォームの例を考えてみましょう。このパターンは、単純なメモから複雑なエンタープライズソリューションまで、あらゆるアプリケーションで見られます。

swift
struct LoginView: View {
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var isLoading: Bool = false
    @State private var errorMessage: String?

    var body: some View {
        Form {
            TextField("Email", text: $email)
            SecureField("Password", text: $password)
            Button("ログイン") {
                login()
            }.disabled(isLoading)
        }
    }

    private func login() {
        isLoading = true
        // ネットワークリクエストを実行
    }
}

この例では、4つの@Stateプロパティがあります:emailとpassword(フォームフィールド用)、isLoading(ローディング表示用)、errorMessage(エラー表示用)。各プロパティは独立してインターフェースの一部を管理します。isLoadingが変更されると、ボタンはdisabled(isLoading)によって自動的に無効になります — 手動のUI更新は不要です。

よくある質問

なぜ@Stateはprivateで宣言されるのですか?

@Stateは特定のビューのローカルステート用に設計されています。private修飾子は、他のコンポーネントが直接変更してカプセル化を壊すことを防ぎます。外部アクセスには$プロジェクションを使用してください。

@Stateは配列や辞書を保持できますか?

はい、@Stateは配列や辞書をサポートしています。これらは値型だからです。ただし、配列の要素が変更されると、SwiftUIはビュー全体を再描画します。大きなリストの場合は、@Published付きの@StateObjectの方が効率的です。

Optional型の@Stateプロパティにnilを代入するとどうなりますか?

@StateはOptional型で正しく動作します。nilが代入されると、SwiftUIは変更を検出してビューを再描画します。これはerrorMessage: String?のようなステートに便利で、nilはエラーがないことを意味します。

ビューが再表示されたとき、@Stateはどう動作しますか?

@Stateは、ビューが階層に残っている限り値を保持します。ビューが階層から削除されて再度追加されると、@Stateはデフォルト値で再初期化されます。永続性が必要な場合は、@AppStorageを使用してください。

@Stateの変更をアニメーション化できますか?

はい、変更をwithAnimationでラップします:withAnimation(.easeInOut) { isExpanded.toggle() }。SwiftUIは指定されたアニメーションタイプで、古いインターフェースステートから新しいステートへの遷移をアニメーション化します。

まとめ

  • @State — 1つのビューのローカルステート用Property Wrapper、インターフェースを自動更新
  • 対応 単純な型:String、Int、Bool、struct、enum
  • 非対応 参照型(クラス) — @StateObjectを使用
  • 常にprivate — ステートは外部から直接変更されるべきではない
  • $プロジェクション — 子ビューに変更権限を渡すBindingを作成
  • 複数の@State 1つのビュー内 — 独立したステートには通常の方法
  • withAnimation — @Stateプロパティの変更をアニメーション化可能

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください