#author("2022-11-16T04:34:52+00:00","default:src128","src128") #author("2022-11-16T05:15:09+00:00","default:src128","src128") &tag(SwiftUI/PropertyWrapper); *目次 [#z8da1a17] #contents *関連ページ [#n88c2da1] *参考情報 [#ua83d4c0] -[[SwiftUIのデータ管理 Property Wrapper編:https://blog.personal-factory.com/2021/01/23/how-to-use-propertywrapper-in-swiftui/]] *Property Wrapperの種類 [#l9739a89] -@State、@Binding、@StateObject、@ObservedObjectなどデータを管理するためのProperty Wrapperがたくさん存在する ** @State [#b535e418] -データが値型。データの更新あり。データの発生源がVIewの場合に使用。 -ボタンをタップするとその数字が増える。 #pre{{ struct StateSample: View { private var counter = 0 var body: some View { Button(action: { self.counter += 1 }, label: { Text("counter is \(counter)") }) } } }} ** @Binding [#pd81fb92] -データが値型。データの更新あり。データの発生源は外部。 #pre{{ struct ParentView: View { @State private var counter = 0 var body: some View { ChildView(counter: $counter) .frame(width: .infinity) } } struct ChildView: View { @Binding var counter: Int var body: some View { Button(action: { counter += 1 }, label: { Text("\(counter)") .font(.title) }) .border(Color.red) } } }} ** @Environment [#j1aa4f77] -Viewの環境値を読み取れるもの。KeyPathを指定する。 #pre{{ struct EnvironmentSample: View { @Environment(\.colorScheme) var colorScheme: ColorScheme var body: some View { if colorScheme == .dark { Text("dark mode") } else if colorScheme == .light { Text("light mode") } else { Text("") } } } }} ** ObservableObjectプロトコル [#oc30aca6] -@StateObject、@ObservedObject、@EnvironmentObjectとして使用するデータオブエクトに実装。 #pre{{ class DataSource: ObservableObject { @Published var counter = 0 } }} ** @StateObject [#g4dfa573] -データが参照型のデータオブジェクト。データの発生源がVIewの場合に使用。 -iOS 14以降。 #pre{{ struct StateObjectSample: View { @StateObject private var dataSource = DataSource() var body: some View { VStack { Button("increment counter") { dataSource.counter += 1 } Text("StateObject count: \(dataSource.counter)") .font(.title) } } } }} ** @ObservedObject [#w79717f5] -データが他参照のデータオブジェクト。データの発生源が外部。 #pre{{ fileprivate struct ParentView: View { @StateObject private var dataSource = DataSource() var body: some View { ChildView(dataSource: dataSource) } } fileprivate struct ChildView: View { @ObservedObject var dataSource: DataSource var body: some View { VStack { Button("increment counter") { dataSource.counter += 1 } Text("count: \(dataSource.counter)") } } } } } ** @StateObjectと@ObservedObjectの違い -以下のサンプルを考える。 #pre{{ struct StateObjectCounterView: View { @StateObject private var dataSource = DataSource() var body: some View { VStack { Button("increment counter") { dataSource.counter += 1 } Text("StateObject count: \(dataSource.counter)") .font(.title) } } } struct ObservedObjcetCounterView: View { @ObservedObject private var dataSource = DataSource() var body: some View { VStack { Button("increment counter") { dataSource.counter += 1 } Text("ObservedObject count: \(dataSource.counter)") .font(.title) } } } struct SwitchColorView: View { @State private var isDanger: Bool = false var body: some View { VStack { Button("Change the Color") { isDanger.toggle() } if isDanger { Circle().foregroundColor(.red) .frame(width: 200, height: 200) } else { Circle().foregroundColor(.green) .frame(width: 200, height: 200) } StateObjectCounterView() ObservedObjcetCounterView() Spacer() } } } }} -StateObjectとObservedObjectをカウントアップした後、「Change the Color」で色を変更すると、StateObjectのカウントは維持されるが、ObservedObjectはリセットされる。 -@StateObjectはViewを表示してから非表示まで。@ObservedObjectはbodyが更新されるまでのため。 -これを避けるためには、@ObservedObjectには親Viewからわたす。 **@EnvironmentObject [#s5554de0] -参照型オブジェクトを子ビューでアクセスできるようになる。 #pre{{ fileprivate struct ParentView: View { var body: some View { ChildView() } } fileprivate struct ChildView: View { var body: some View { GrandChildView() } } fileprivate struct GrandChildView: View { @EnvironmentObject var dataSource: DataSource var body: some View { Text("\(dataSource.counter)") } } struct EnvironmentObjectSample_Previews: PreviewProvider { @StateObject static private var dataSource = DataSource() static var previews: some View { ParentView().environmentObject(dataSource) } } }}