はじめに
SwiftUIの@Binding
について学習したので備忘録とアウトプットのつもりで書いてみます。
作成するアプリ
トグルが2つチェックされたときと1つでもチェックされていない時でテキストがかわるアプリを作ります。
とりあえず作成する
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またはコメントで"やさしく"教えてくださいませ。