【SwiftUI】@Bindingを理解したい

# はじめに
SwiftUIの`@Binding`について学習したので備忘録とアウトプットのつもりで書いてみます。

# 作成するアプリ
トグルが２つチェックされたときと１つでもチェックされていない時でテキストがかわるアプリを作ります。
![Simulator-Screen-Recording-iPhone-14-Pro-2023-04-09-at-02.15.22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/1bb06cf6-b9a8-3b8b-1b3a-2d4a99439576.gif)

# とりあえず作成する
ToggleとToggleのチェック状態に合わせての変更されるTextを合わせたCustomToggleというViewを作成します。

このアプリでやりたいことは、トグルのチェック状態によってチェックマークの変更とカスタムトグル２つの状態によってContentViewのテキストの内容を変更することです。

```swift
struct CustomToggle: View {
    let isChecked: Bool
    var body: some View {
        HStack {
// Cannot convert value of type 'Bool' to expected argument type 'Binding<Bool>'
            Toggle("", isOn: isChecked).labelsHidden()
            Text(isChecked ? "✅" : "❎").font(.title)
        }
    }
}

struct ContentView: View {
    private var isChecked1 = false
    private var isChecked2 = false
    var body: some View {
        VStack {
            CustomToggle(isChecked: isChecked1)
            CustomToggle(isChecked: isChecked2)
            Text("テキスト")
        }
    }
}
```

エラーがToggleの箇所で出てきます。
Toggleの[こちら](https://developer.apple.com/documentation/swiftui/toggle/init(_:ison:)-2qurm)のイニシャライザを使用します。CustomToggleのisCheckedはbool型でありトグルがオンかオフかを示すプロパティへのバインディングを行うためBinding型にしないといけません。

# `@Binding`とは
[ドキュメント](https://developer.apple.com/documentation/swiftui/binding)
`@Binding`は、Binding型を扱うための PropertyWrapper です。

今回の例だとトグルの状態を設定する変数isCheckedと各トグルの状態を保存する変数isChecked1、isChecked２とをどのようにバインディングするのか、つまり、別のビューの変数の現在値をどのように得るかを可能にするプロパティラッパーです。

isCheckedに`@Binding`をつけます。
すると、$をつけてくださいというエラーが出てきます。
$をつけてアクセスするということは、Binding型にprojectedValueをつけてアクセスすることと等価です。
[こちら](https://software.small-desk.com/development/2020/10/03/swift-propertywrapper-projectedvalue/)にわかりやすい記事があります。
今回の例だと、Toggleの方ではユーザーの入力によってToggleのBool値がisCheckedに設定されるので、＄が必要となり、Textの方は単純にisCheckedの値を参照するだけなので$は必要ありません。

```swift
struct CustomToggle: View {
    @Binding var isChecked: Bool
    var body: some View {
        HStack {
            Toggle("", isOn: isChecked).labelsHidden()
            Text(isChecked ? "✅" : "❎").font(.title)
        }
    }
}
```

それぞれのToggleの状態を管理する変数isChecked1, isChecked2の参照をCustomToggleのisCheckedに渡してバインドします。
isChecked1とisChecked2の状態によってTextの文字を変更します。
[allSatisfy](https://developer.apple.com/documentation/swift/array/allsatisfy(_:))

```swift
struct ContentView: View {
    @State private var isChecked1 = false
    @State private var isChecked2 = false
    var body: some View {
        VStack {
            CustomToggle(isChecked: $isChecked1)
            CustomToggle(isChecked: $isChecked2)
            if [isChecked1, isChecked2].allSatisfy({ $0 }) {
                Text("全てチェックした").foregroundColor(.red)
            } else {
                Text("未チェックあり").foregroundColor(.blue)
            }
        }
    }
}
```

# なぜ`@State`ではなく`@Binding`を使う
今回の例だと`@Binding`を`@State`にしてもToggleの状態によってチェックマークの状態を変更はできますが、ContentViewにバインディングしていないため、ContentViewではCustomToggleのisCheckedの現在の値を知ることができません。するとトグルの状態によってTextの文字を変更することができないので、`@Binding`を使用する必要があります。

```swift
struct CustomToggle: View {
    @Binding var isChecked: Bool
    var body: some View {
        HStack {
            Toggle("", isOn: $isChecked).labelsHidden()
            Text(isChecked ? "✅" : "❎").font(.title)
        }
    }
}

struct ContentView: View {
    @State private var isChecked1 = false
    @State private var isChecked2 = false
    var body: some View {
        VStack {
            CustomToggle(isChecked: $isChecked1)
            CustomToggle(isChecked: $isChecked2)
            if [isChecked1, isChecked2].allSatisfy({ $0 }) {
                Text("全てチェックした").foregroundColor(.red)
            } else {
                Text("未チェックあり").foregroundColor(.blue)
            }
        }
    }
}
```

# おわりに
SwiftUI学習始めたばかりなので、間違ってる点がありましたらTwitterまたはコメントで"やさしく"教えてくださいませ。