@Stateは、1つのビュー内でローカルステートを管理するためのSwiftUIのProperty Wrapperです。@Stateプロパティが変更されるたびに、SwiftUIは自動的にビューを再描画し、手動の更新呼び出しなしでインターフェースをリアクティブにします。Apple Developer Documentation (2025)によると、@Stateは1つのビューに属する単純な型や構造体に推奨されます。@Stateは、SwiftUIインターフェースにインタラクティビティを追加する最も簡単な方法です。
重要なポイント
@StateはSwiftUIに組み込まれたProperty Wrapperで、ビューが自身のステートを保存および追跡できるようにします。@Stateの値が変更されると、SwiftUIはbodyプロパティを再実行してビューを自動的に再描画します。これがSwiftUIにおけるリアクティブプログラミングの基礎です。開発者はステートを宣言し、フレームワークがインターフェースの同期を処理します。
@Stateは、SwiftUIが管理するヒープ上にストレージ領域を作成します。この領域は永続的であり、レンダリングのたびに発生するビュー構造体の繰り返し初期化を乗り越えて存続します。SwiftUIは、ビューの識別子(階層内の位置から生成)を使用して、@Stateプロパティを特定のビューにバインドします。これにより、親ビューが更新されてもステートがリセットされません。
重要な制限:@Stateは値型(struct、enum、プリミティブ)のみを対象としています。参照型(クラス)の場合は、@StateObjectまたは@ObservedObjectを使用してください。@Stateプロパティにクラスを割り当てると、SwiftUIはオブジェクト内の変更を検出できません — 参照の完全な置き換えのみを検出します。
SwiftUIは@Stateを内部のStorageメカニズムを通じて実装しています。各@Stateプロパティは、ビューの特別なストレージコンテナに保存される専用のメモリセルを取得します。wrappedValueへの書き込みが発生すると、SwiftUIはdidSetを介して依存関係グラフに再描画の必要性を通知します。
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は単純なローカルステートに最適です:検索のテキストフィールド、モーダルウィンドウのブールフラグ、設定のトグル、カウンター、選択されたリストアイテム。値が1つのビューとその子コンポーネント(@Binding経由)でのみ使用される場合、@Stateが正しい選択です。ビューが閉じられても存続すべきステート(フォームデータなど)の場合、ビューが階層に残っている限り@Stateも機能します。
@Stateをグローバルなアプリケーションステート、ネットワークデータのキャッシュ、または複数の画面で使用されるオブジェクトには使用しないでください。@StateObjectと@EnvironmentObjectがこれらの目的のために設計されています。また、@Stateは大量のデータの保存には適していません — 変更のたびにビュー全体が再描画されます。
@Bindingは、親ビューの@Stateとそのステートを変更する必要がある子ビューとの間の橋渡しです。親が@Stateを宣言し、子コンポーネントは$プロジェクションを介してBindingを受け取ります。子ビューでBindingを変更すると、親の@Stateが自動的に更新され、その逆も同様です。これにより、フィードバック機能を備えた単一方向のデータフローが確保されます。
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 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つの構造体に結合してください。これにより、子ビューへのステートの受け渡しが簡素化され、個別の更新トリガーの数が減ります。
@Stateは、ほとんどのSwiftUIプロジェクトで基本的なインタラクティビティに使用されています。@Stateがテキストフィールドとローディングステートを管理するログインフォームの例を考えてみましょう。このパターンは、単純なメモから複雑なエンタープライズソリューションまで、あらゆるアプリケーションで見られます。
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は配列や辞書をサポートしています。これらは値型だからです。ただし、配列の要素が変更されると、SwiftUIはビュー全体を再描画します。大きなリストの場合は、@Published付きの@StateObjectの方が効率的です。
@StateはOptional型で正しく動作します。nilが代入されると、SwiftUIは変更を検出してビューを再描画します。これはerrorMessage: String?のようなステートに便利で、nilはエラーがないことを意味します。
@Stateは、ビューが階層に残っている限り値を保持します。ビューが階層から削除されて再度追加されると、@Stateはデフォルト値で再初期化されます。永続性が必要な場合は、@AppStorageを使用してください。
はい、変更をwithAnimationでラップします:withAnimation(.easeInOut) { isExpanded.toggle() }。SwiftUIは指定されたアニメーションタイプで、古いインターフェースステートから新しいステートへの遷移をアニメーション化します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。