SwiftUIでデータバインディング
データバインディングとは?
データバインディングとは、データと対象を結びつけて、データまたは対象の変更を暗示的にもう片方にに反映させること。例えば、アプリ上のUIを操作すると、その変更がデータに反映されたり、データを更新するとUIも再描画される。
SwiftUIでデータバインディング
SwiftUIのデータバインディングの仕組み:
@State:プロパティとViewを紐付ける
@ObservedObject:データクラスとViewを紐付ける(Viewの再構築でインスタンスが破棄される)
@StateObject:データクラスとViewを紐付ける(Viewの再構築でもインスタンスが破棄されない)
@EnvironmentObject:データクラスとViewを紐付ける(複数Viewやアプリケーション全体に渡ってデータを共有できる)
@State
SwiftUIのViewはstructなので、保持するプロパティを変更できないが、@Stateを付けたプロパティは、SwiftUIフレームワークによって監視され、プロパティの値とUIの状態が同期される。
ただし、そのプロパティは宣言されたView内からアクセスできないので、子Viewからは@Bindingでプロパティを宣言すると参照でき、値の更新も親Viewに反映される。
struct ContentView: View {
// ON/OFFの状態をプロパティで保持する
@State private var isOn = false
var body: some View {
VStack {
// isOnの参照を渡して、スイッチのON/OFF時にisOnが更新される
Toggle(isOn: $isOn) {
Text("設定")
}
// isOnの値で表示テキストを変更
Text(isOn ? "設定ON" : "設定OFF")
}
}
}
// 親View
struct ContentView: View {
@State private var isOn = false
var body: some View {
VStack {
// 子ViewにisOnの参照を渡す
SwitchView(isOn: $isOn)
Text(isOn ? "設定ON" : "設定OFF")
}
}
}
// 子View
struct SwitchView: View {
// 親Viewのプロパティへの参照を宣言
@Binding var isOn: Bool
var body: some View {
// 子ViewでisOnが変更されれば、親ViewのisOnも変更される
Toggle(isOn: $isOn) {
Text("設定")
}
}
}
@ObservedObject
プロパティではなくデータクラス(classインスタンス)とViewを紐付ける。インスタンスのプロパティの変化を監視し、値が変化するとViewが再描画される。しかし、Viewが再構築されると、インスタンスが初期化されてしまう。
対象クラスは
・ObservableObjectプロトコルに準拠する必要あり
・監視対象のプロパティは@Published属性を付与する必要あり
struct ContentView: View {
// バインドしたいデータクラスのインスタンスを@ObservedObjectで宣言
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
Toggle(isOn: $viewModel.isOn) {
Text("設定")
}
Text(viewModel.isOn ? "設定ON" : "設定OFF")
}
}
}
// データクラス:ObservableObjectプロトコルに準拠
class ViewModel: ObservableObject {
// バインドしたいプロパティを@Publishedで宣言
@Published var isOn = true
}
@StateObject
@ObservedObjectと同様に、データクラスとViewを紐付けるが、Viewが再構築されてもデータクラスのインスタンスが初期化されずに保持される。
// 親View
struct ContentView: View {
// バインドしたいデータクラスのインスタンスを@StateObjectで宣言
@StateObject var viewModel = ViewModel()
var body: some View {
VStack {
SwitchView(viewModel: viewModel)
Text(viewModel.isOn ? "設定ON" : "設定OFF")
}
}
}
// 子View
struct SwitchView: View {
// 親Viewから渡されるので@ObservedObjectで宣言
@ObservedObject var viewModel: ViewModel
var body: some View {
Toggle(isOn: $viewModel.isOn) {
Text("設定")
}
}
}
class ViewModel: ObservableObject {
@Published var isOn = true
}
@EnvironmentObject
@ObservedObjectと同様に、データクラスとViewを紐付けるが、子Viewに渡さなくても配下のViewからアクセスできる。階層トップのViewに紐付けると、アプリケーション全体からアクセスできるので、アプリケーション全体でデータ共有が必要な場合に使える。
// 親View
struct ContentView: View {
// 共有するデータクラスのインスタンスを宣言
@EnvironmentObject var setting: Setting
var body: some View {
VStack {
SwitchView()
Text(setting.isOn ? "設定ON" : "設定OFF")
}
}
}
// 子View
struct SwitchView: View {
// 共有するデータクラスのインスタンスを宣言
@EnvironmentObject var switchSetting: Setting
var body: some View {
// インスタンスを共有しているので、ここで変更されると親Viewにも反映される
Toggle(isOn: $switchSetting.isOn) {
Text("設定")
}
}
}
// データクラス:ObservableObjectプロトコルに準拠
class Setting: ObservableObject {
@Published var isOn = true
}
@main
struct testApp: App {
var body: some Scene {
WindowGroup {
// データクラスのインスタンスをバインドする
ContentView().environmentObject(Setting())
}
}
}
参考
この記事が気に入ったらサポートをしてみませんか?