この記事は何?
SwiftUIフレームワークのBindableについて、Appleの開発者向けドキュメントを独自に解説する。
Swiftを基礎から学ぶには
自著、工学社より発売中の「まるごと分かるSwiftプログラミング」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。
Bindable構造体
「観測可能なオブジェクトにある変数プロパティへの参照」のバインディングを作成するプロパティラッパー型。
@dynamicMemberLookup @propertyWrapper
struct Bindable<Value>
概要
@Bindable
プロパティラッパーを使用すると、Observable
プロトコルに準拠したデータモデルオブジェクトの変数プロパティへのバインディングを作成できる。
単純なケース
以下の例において、データモデルのBook
クラスは「信頼できる情報源」。
そして、BookEditView
ビューは変数book
に@Bindable
をマークしている。
そのbody
では、TextField
ビューがbook
のバインドを利用して、title
プロパティを変更する。
また、Toggle
ビューはbook
のisAvailable
プロパティを変更する。
ここでは、各プロパティのバインディングを渡すために、どちらのコントロールビューに対しても$
構文を使用している点に注目。
// 観測可能なオブジェクトのデータモデル
@Observable
class Book: Identifiable {
var title = "Sample Book Title"
var isAvailable = true
}
// 図書の情報を編集するためのビュー
struct BookEditView: View {
@Bindable var book: Book // bookは信頼できる情報源にバインド
@Environment(\.dismiss) private var dismiss
// TextFieldとToggleは「bookへのバインド」を受け取る
var body: some View {
Form {
TextField("Title", text: $book.title)
Toggle("Book is available", isOn: $book.isAvailable)
Button("Close") {
dismiss()
}
}
}
}
上のコードにおいて、ビューのプロパティとして作成されるバインド変数には、既定値を設定しない点に注意。
実際のインスタンスは、親ビューから渡される。
ケース2
Bindable
プロパティラッパーは、Observable
オブジェクトの変数およびプロパティにも使用できる。
これにはグローバル変数や「SwiftUIの外部にある型のプロパティ」だけでなく、ローカル変数が含まれる。
例えば、以下のLibraryView
ビューはbody内で@Bindable
変数を作成する。
ボディのリスト内でバインド変数を作成することによって、それぞれの要素を参照するバインドを取得できる。
struct LibraryView: View {
// Book型インスタンスはObservableなデータモデル
@State private var books = [Book(), Book(), Book()]
var body: some View {
List(books) { book in
@Bindable var book = book
TextField("Title", text: $book.title)
}
}
}
ここではまた、@Bindable
な変数に既定値を設定している点に注目できる(最初のケースでは既定値を設定していない)。
@Bindable
な変数book
は「book
データモデルに接続するバインディング」を提供するので、TextField
ビューは「モデルデータのtitle
プロパティ」にアクセスして、変更を加えることができる。
ケース3
ケース2と同じ手法を、「観測可能なオブジェクトへのバインディング」がビューの環境に格納されている場合も使用できる。
例えば、次のコードは@Environment()
プロパティラッパーを使用して、環境にあるObservable
なBook
型インスタンスを取得する。
そして、ビューのbody
内で@Bindable
な変数book
を作成して、$
記号をマークして「book
データモデルのtitle
プロパティまでのバインディング」をTextField
ビューに渡す。
struct TitleEditView: View {
// bookプロパティに「ビューの環境にあるモデルデータ」を取得
@Environment(Book.self) private var book
var body: some View {
@Bindable var book = book // 環境にあるモデルデータの参照をバインド
TextField("Title", text: $book.title)
}
}
ここでもやはり、バインド変数に既定値を設定する必要がある。