SwiftUIにおける変数の扱い方【View間のデータ渡しPreference】
Preference
親Viewから子Viewへの値を通知する手段の一つにEnvironmentがあります。Environmentとは逆で、子Viewから親Viewへ値を渡すための仕組みとしてPreferenceというものがあります。今回は子Viewから親Viewへの値を渡すPreferenceについて紹介していきます。
子Viewから親Viewへデータを渡す仕組み
SwiftUIにおけるNavigationViewのモディフィアである.navigationTitleもPreferenceを利用して実現されていると紹介されています。
NavigationViewのモディフィア:.navigationTitleのサンプル
NavigationViewのカッコ内でのみ有効になります。
struct ContentViewNavigationView: View {
var body: some View {
NavigationView {
VStack{
Text("Hello, World!")
// .navigationTitle("タイトル") // OK
}
.navigationTitle("タイトル") // OK
}
// .navigationTitle("タイトル") // ここでのコールはNG
}
}
使用方法
Preferenceを使用するにはPreferenceKeyプロトコルに準拠したStructを定義します。
1.プロトコル:PreferenceKeyに準拠したPreferenceを定義。
struct BoolPreference: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = false
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValuer()
}
}
2.子ビュー
.preference()を使用して定義したPreferenceにデータをセット
sttruct StateView: View {
var body: some View {
VStack {
Text("hello")
}.preference(key: BoolPreferece.self, value: true)
}
}
親ビュー
定義したPreferenceの値の変更を.onPreferenceChangeにより処理します。
struct GameView: View {
@State var state: Bool
var body: some View {
StateView()
.onPreferenceChange(BoolPrefeerence.self) { value in
self.state = value
}
}
}
onPreferenceChangeは.preferenceモディフィアを使用してPreferenceKeyの値を変更したときにコールされます。
サンプルコード
親ViewでBool値の状態によりアイコンを表示、子ViewのOn/Offボタンタップを親Viewに通知するサンプル
親View
struct ContentViewPreference: View {
@State var state: Bool = true
@State var text: String = ""
var body: some View {
VStack {
HStack {
Button(action: {
}) {
Image(systemName: state ? "speaker.wave.3.fill" : "speaker.slash.fill")
}
Text(self.text)
}
ContentViewPreferenceChild()
.onPreferenceChange(BoolPreference.self) { value in
self.state = value
}
}
}
}
子View
struct BoolPreference: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = true
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}
}
struct ContentViewPreferenceChild: View {
@State var state: Bool = true
var body: some View {
HStack {
Button("On") {
state = true
}
Button("Off") {
state = false
}
if state {
Text("")
.preference(key: BoolPreference.self, value: true)
} else {
Text("")
.preference(key: BoolPreference.self, value: false)
}
}
}
}