@Binding — その概要、動作方法、SwiftUIでの例

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

@Bindingは、別のコンポーネントが所有するデータへの参照を作成するSwiftUIのProperty Wrapperです。Bindingは値自体を保存せず、$プロジェクションを通じて既存のデータソースへのアクセスのみを提供します。Apple Developer Documentation (2025)によると、Bindingはデータを直接所有せずに、親ビューと子ビューの間でリアクティブな双方向通信を提供します。@Bindingは、可変状態を階層の下位に渡すための主要なメカニズムです。

重要なポイント

  • @Binding — 親ビューの状態への参照を作成するProperty Wrapper
  • 所有権なし — Bindingはデータを保存せず、データソースへのアクセスのみを提供
  • $プロジェクション — $stateValueが@Stateまたは@StateObjectからBindingを作成
  • 双方向バインディング — 子ビューの変更が即座に親に反映される
  • Binding.constant — フィードバックなしのプロトタイピング用固定値

SwiftUIの@Bindingとは?

@Bindingは、親ビューに保存されたプロパティと子コンポーネントの間に双方向の接続を作成するProperty Wrapperです。Bindingと@Stateの主な違い:Bindingはデータを所有しません。真のデータソース(@State、@StateObject、または親の別のBinding)を介して値の読み書きのみを行います。Bindingがないと、子ビューはコールバックやデリゲートなしで祖先の状態を変更できません。

Bindingは2つのプロパティを持つ構造体として実装されます:wrappedValue(現在の値)とprojectedValue(Binding自体、$を介してアクセス可能)。子ビューがBindingを介してwrappedValueを変更すると、SwiftUIはデータソースに変更を伝達し、依存するすべてのビューを再描画します。これは現在の更新サイクル内で同期的に発生します。

重要な特徴:@Bindingは単一レベルの受け渡しに限定されません。Bindingは複数の階層レベルを通過して渡すことができ、各子コンポーネントは同じデータソースへの参照を受け取ります。任意のレベルでの変更は、バインドされたすべてのビューの単一の更新をトリガーします。

Bindingの双方向バインディングの仕組み

@Bindingによる双方向バインディングのメカニズムは、Property Wrapperのプロジェクションに基づいています。親が@State var value: Tを宣言すると、SwiftUIは自動的にBinding<T>型の$valueプロジェクションを生成します。$valueを@Binding var value: Tを持つ子コンポーネントに渡すことで、両方のビューを同じメモリセルに接続します。子ビューでのBindingを介した書き込みは、両方のコンポーネントの再描画をトリガーします。

swift
struct SliderContainer: View {
    @State private var value: Double = 0.5

    var body: some View {
        VStack {
            Text("Value: \(value)")
            SliderView(value: $value)
        }
    }
}

struct SliderView: View {
    @Binding var value: Double

    var body: some View {
        Slider(value: $value, in: 0...1)
    }
}

この例では、SliderContainerが@State valueを所有し、SliderViewが$valueを介してBindingを受け取ります。SliderView内のSliderはこのBindingにバインドされています。スライダーをドラッグすると、SliderはBindingを介して値を変更し、SliderContainerの@Stateを自動的に更新して、両方のビューが現在の数値を表示します。チェーン全体が単一のコールバックや通知なしで機能します。

@StateObjectまたは@ObservedObjectからBindingを作成するには、同じプロジェクションを使用します:$object.propertyはBinding<PropertyType>を提供します。これにより、オブジェクト全体を渡さずにObservableObjectの個別のプロパティを子ビューに渡すことができます。このアプローチはより狭い結合を提供し、不要な再描画を防ぎます。

@Binding vs コールバック:どちらを選ぶべきか

SwiftUI以前は、階層の上位に変更を渡す標準的な方法はコールバックとデリゲートでした:親がクロージャを渡し、子コンポーネントが変更時にそれを呼び出します。@Bindingはより少ないコードとより宣言的な構文で代替手段を提供します。完了クロージャを渡す代わりに、$stateValueを渡すだけです。

基準@Bindingコールバック
コード1つのアノテーション + $クロージャ + 呼び出し
マルチレベル自動クロージャチェーン
テストBinding(value:constant)モッククロージャ
可読性高い中程度
柔軟性データのみ任意のロジック

子ビューが値の読み取りと変更のみを必要とする場合は@Bindingを使用します。変更時に副作用(検証、ロギング、ネットワークリクエスト)が必要な場合は、Bindingとコールバックを組み合わせます:データ用のBindingとイベント用のクロージャを渡します。例えば、TextFieldはBindingにバインドし、onChangeが検証をトリガーします。

プロジェクトでの@Bindingの使用パターン

@Bindingはいくつかの典型的なシナリオで使用されます。1つ目はカスタムコントロール:スイッチ、スライダー、カラーピッカーなどのインタラクティブ要素が双方向同期のためにBindingを受け入れます。2つ目はモーダルウィンドウ:シート表示フラグがBindingとして渡され、子ビューがpresentationModeまたは直接設定で自身を閉じることができます。

3つ目のパターンは分離されたフォームです。フォームが多くのフィールドで構成されている場合、各フィールドを個別のコンポーネントに抽出して、その値のBindingを受け入れることができます。これにより、異なるフォーム間でのフィールドのテストと再利用が簡素化されます。親コンポーネントはフォームモデル全体の唯一の所有者であり続けます。

swift
struct FormField: View {
    let title: String
    @Binding var text: String

    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.caption)
            TextField("Enter \(title.lowercased())", text: $text)
                .textFieldStyle(.roundedBorder)
        }
    }
}

FormFieldコンポーネントはタイトルと文字列へのBindingを受け入れます。ラベルと、渡されたBindingにバインドされたTextFieldを表示します。任意のフォームは、各フィールドに$propertyを渡すことでFormFieldを複数回使用できます。これによりマークアップの重複が減り、テキストフィールドのスタイリングが集中化されます。

カスタムBindingの作成

SwiftUIでは、Binding(get:set:)イニシャライザを使用して手動でBindingを作成できます。これは値の読み取りまたは書き込み時にロジックを追加する必要がある場合に便利です。例えば、保存前に数値をフォーマットするBindingや、変更のたびにリモートサーバーと値を同期するBindingを作成できます。

swift
struct ValidatedField: View {
    @State private var email: String = ""

    var emailBinding: Binding<String> {
        .init(
            get: { email },
            set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
        )
    }

    var body: some View {
        TextField("Email", text: emailBinding)
    }
}

リストでは、カスタムemailBindingが変更のたびに自動的にテキストを小文字に変換し、スペースをトリミングします。TextFieldは$emailに直接バインドする代わりにこのBindingを使用します。このアプローチにより、onChangeハンドラでコードを散らかすことなく、Binding内で検証とデータ変換を集中化できます。

@Bindingのエラーとアンチパターン

最初で最も一般的なエラーはBindingの代わりに値を渡すことです。子コンポーネントが@Binding var text: Stringを宣言し、親がtext($なし)を渡すと、コンパイラはエラーを出します:Cannot convert value of type 'String' to expected argument type 'Binding<String>'。解決策は、渡すときに常に$プレフィックスを使用することです:$text。

2つ目のエラーは読み取り専用データへのBindingです。子ビューが値の読み取りのみを必要とする場合、@Bindingは使用しないでください。親からの単純なletまたは@Stateで十分です。Bindingは書き込み可能性を意味し、過剰な変更権限はデバッグを複雑にし、最小権限の原則に違反します。

3つ目の問題は本番環境でのBinding.constantです。Binding.constant(value)はフィードバックなしのダミーバインディングを作成し、変更は無視されます。constantはプロトタイピングとプレビュー(Xcode Previews)にのみ使用し、実際のコードでは決して使用しないでください。テストには、制御された動作のBinding(get:set:)を使用します。

よくある質問

@Bindingと@Stateの違いは何ですか?

@Stateはデータを所有し、ヒープ内のその保存を管理します。@Bindingは所有権なしで既存の状態を参照するだけです。@Stateは常にプライベートで、@Bindingは子ビューの入力パラメータです。

@StateなしでBindingを作成できますか?

はい、Binding(get:set:)イニシャライザまたはBinding.constant(value)を使用して作成できます。Bindingは@StateObjectから$object.$propertyプロジェクションを介して、またPublisherからSubscribe内のBinding(get:set:)を介して取得することもできます。

複数のネストレベルを介してBindingを渡すにはどうすればよいですか?

@Bindingはチェーンを介して渡されます:各中間コンポーネントが@Bindingを宣言し、$を介してさらに渡します。すべてのレベルがルートビューの同じデータソースを参照します。

Binding.constantがインターフェースを更新しないのはなぜですか?

Binding.constantはミュートラッパーを作成し、セッターは新しい値を無視します。これは子コンポーネントからのフィードバックが不要なプロトタイピングとSwiftUI Previewsのためだけのものです。

@Bindingをオプショナルにできますか?

はい、Binding<T?>がサポートされています。Binding<String?>を渡すと、子ビューはnilを設定できます。これはオプションのフォームフィールドやリセットオプションのある状態に便利です。

まとめ

  • @Binding — 親ビューデータとの双方向バインディング用Property Wrapper
  • 所有しないデータ — データソースへのアクセスのみ提供
  • $プロジェクションが@State、@StateObjectを子ビューに渡すBindingに変換
  • カスタムBindingは追加ロジックを持つBinding(get:set:)で作成
  • Binding.constant — プレビューとプロトタイプのみ
  • マルチレベル受け渡し — Bindingは任意のネスト深度を通過
  • コールバックの代替 — 子コンポーネントから状態を変更する宣言的方法

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

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

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

こちらもお読みください