Property Wrapperとは?
Property Wrapperとは一言でいうと、SwiftUIが変数の状態を自動で監視してくれる仕組みのことです。
Property Wrapperを使うことで、複数のViewで変数を共有したり、変数が更新されるとViewを再描画してくれます。
State
1.プロパティが更新可能になる。
2.プロパティが変更されるとビューも同時に更新される。
解説
通常SwiftUIのビューは、Structの為プロパティを更新することができません。しかし、@Stateを付けることで更新が可能になります。
また、@Stateをつけたプロパティは、SwiftUIによって監視されている為、変更があればビューを自動的に再描画してくれます。
Binding
1.別のビュー同士の変数を紐づける。
解説
Bindingは少しややこしいので、子ビューから親ビューの変数の値を変更する簡単なコードを使って説明します。
まず以下のような親ビューAと子ビューBがあるとします。
import SwiftUI
struct ParentView: View {
@State var text = "初期の表示です。"
@State var isShowView = false
var body: some View {
VStack {
Text(text)
.padding()
Button("画面Bへ") {
isShowView = true
}
}
.sheet(isPresented: $isShowView) {
ChildView(text: $text)
}
}
}
struct ChildView: View {
@Binding var text: String
var body: some View {
Button("このボタンを押すとメッセージが変わります。") {
text = "文字が変更されたよ!"
}
}
}
通常、Aの親ビューの中で定義された変数の値は、Bの子ビューから知ることができません。
しかし、@Bindingを使うことでそれが可能になります。
また、Bのビューで変数を変更することでAのビューの変数も変更されます。
上の例だと、ChildViewの中のボタンを押すことで、text変数に文字が入りAの画面に戻ると文字が変更されています。
つまり、@Bindingをつけることで親と子の双方向でのやり取りが可能になります。
StateObject & ObservedObject
1.どちらもオブジェクトの状態を監視するプロパティラッパ
2.違いはビューのライフサイク
解説
StateObjectとObservedObjectの違いはビューのライフサイクです。
- StateObject:Viewに対してインスタンスが1つだけ生成される
- ObservedObject:Viewのライフサイクルに依存し、Viewの再描画でインスタンスを再生成する
EnvironmentObject
1.アプリケーション全体から共通のオブジェクトにアクセスできる。
解説
ObserverdObjectなどは、値を子ビューに引き渡さなければ値を参照できないが、
EnvironmentObjectを付与すると、子ビューに引き渡さなくても共通の値を参照できるようになります。
例)以下のようなオブジェクトとViewがあったとします。
class UserViewModel: ObservableObject {
@Published var name = "ルフィ"
@Published var age = 19
}
struct View1: View {
@EnvironmentObject var UserViewModel: UserViewModel
var body: some View {
VStack{
Button(action: {
UserViewModel.name = "ルフィ"
UserViewModel.age = 19
}){
Text("ルフィを表示")
}
Text("\(UserViewModel.name)")
Text("\(UserViewModel.age)")
}
}
}
struct View2: View {
@EnvironmentObject var UserViewModel: UserViewModel
var body: some View {
VStack{
Button(action: {
UserViewModel.name = "ゾロ"
UserViewModel.age = 21
}){
Text("ゾロを表示")
}
Text("\(UserViewModel.name)")
Text("\(UserViewModel.age)")
}
}
}
struct MainView: View {
@EnvironmentObject var UserViewModel: UserViewModel
var body: some View{
VStack {
View1()
View2()
}
.environmentObject(UserViewModel)
}
}
view1, view2で UserViewModelインスタンスを作成して、それぞれのビューでUserオブジェクトのname・ageプロパティを変更すると、変更するたびに全てのビューの値が更新されます。
上の例だとそれぞれのviewでボタンを押すと名前と年齢が変わるようにしました。
おわりに
Property Wrapperは、まだまだ勉強中なので間違っていたらご指摘お願いいたします