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)
      }
    }
  }
}

参考文献

    コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です

    CAPTCHA