LoginSignup
1
3

More than 1 year has passed since last update.

【SwiftUI】@Bindingを理解したい

Posted at

はじめに

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

作成するアプリ

トグルが2つチェックされたときと1つでもチェックされていない時でテキストがかわるアプリを作ります。
Simulator-Screen-Recording-iPhone-14-Pro-2023-04-09-at-02.15.22.gif

とりあえず作成する

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

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

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のこちらのイニシャライザを使用します。CustomToggleのisCheckedはbool型でありトグルがオンかオフかを示すプロパティへのバインディングを行うためBinding型にしないといけません。

@Bindingとは

ドキュメント
@Bindingは、Binding型を扱うための PropertyWrapper です。

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

isCheckedに@Bindingをつけます。
すると、$をつけてくださいというエラーが出てきます。
$をつけてアクセスするということは、Binding型にprojectedValueをつけてアクセスすることと等価です。
こちらにわかりやすい記事があります。
今回の例だと、Toggleの方ではユーザーの入力によってToggleのBool値がisCheckedに設定されるので、$が必要となり、Textの方は単純にisCheckedの値を参照するだけなので$は必要ありません。

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

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を使用する必要があります。

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またはコメントで"やさしく"教えてくださいませ。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3