【Swift UI】PreferenceKeyの使い方!onPreferenceChangeメソッドとは?

この記事からわかること

  • Swift UIPreferenceとは?
  • PreferenceKeyプロトコル使い方
  • preference(key:,value:)メソッドonPreferenceChangeメソッドの使い方
  • からデータを渡す方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

参考文献:公式リファレンス:

Preferenceとは?

Swift UIのPreference異なるビュー間でデータを共有するための機能を提供する仕組みです。Preferenceを使用することで子から親へデータを渡すことが可能になります。

Swift UIでも実際に使用されておりnavigationTitle(_:)モディファイアなどが実際にPreferenceを使用している具体例としてあげられています。

NavigationStack {
  List {
      Text("要素1")
      Text("要素2")
  }.navigationTitle("タイトル")
}

navigationTitleNavigationStackから呼び出しそうな気がしますが、子から呼び出す仕様になっています。このように子から指定したデータを親側に反映させる時などにPreferenceは使用されます。

Preferenceの実装方法

Preferenceを簡単なデモ機能を作って実装してみます。今回は「スクロール量を計測し出力する」機能を実装してみます。ここでは子側としてGeometryReaderから取得したスクロール量を親側としてクラスのプロパティに格納し出力する流れを実装してみます。

PreferenceKeyプロトコル

公式リファレンス:PreferenceKeyプロトコル

まずは子側から親側にデータを渡すためのインターフェースとしてPreferenceKeyプロトコルに準拠した構造体を実装していきます。

struct ScrollOffsetYPreferenceKey: PreferenceKey {
    static var defaultValue: [CGFloat] = [0]
    static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
        value.append(contentsOf: nextValue())
    }
}

PreferenceKeyプロトコルにはdefaultValueプロパティとreduceメソッドの実装が必要になります。defaultValueプロパティはそのままデフォルト値を、reduceメソッドには値の更新処理を実装します。

preference(key:,value:)メソッドで値を渡す

公式リファレンス:preference(key:,value:)メソッド

子側から親側にデータを渡すためにpreference(key:,value:)メソッドを使用してインターフェースに値を渡します。引数keyにはインターフェース自身を、valueには更新したい値を渡します。

struct ContentView: View {
    @State  private var offsetY: CGFloat = 0
    @State  private var initOffsetY: CGFloat = 0
    
    var body: some View {
        ScrollView() {
            VStack {
                ForEach(1...100, id: \.self) {
                    Text("Item \($0)")
                        .padding()
                }
            }.background(
                GeometryReader { geometry in
                    Color.clear
                        .preference(
                            key: ScrollOffsetYPreferenceKey.self,
                            value: [geometry.frame(in: .global).minY]
                        ).onAppear {
                            initOffsetY = geometry.frame(in: .global).minY
                        }
                }
            )
        }
    }
}

onPreferenceChangeメソッドで変更を検知

公式リファレンス:onPreferenceChangeメソッド

インターフェースから値を取得するためにonPreferenceChangeメソッドを使用して変更を観測します。変化があればクロージャー内から取得できるのであとは自由に操作するだけです。

ScrollView() {
    VStack {
        ForEach(1...100, id: \.self) {
            Text("Item \($0)")
                .padding()
        }
    }.background(
        GeometryReader { geometry in
            Color.clear
                .preference(
                    key: ScrollOffsetYPreferenceKey.self,
                    value: [geometry.frame(in: .global).minY]
                ).onAppear {
                    initOffsetY = geometry.frame(in: .global).minY
                }
        }
    )
}
.onPreferenceChange(ScrollOffsetYPreferenceKey.self) { value in
    offsetY = value[0]
    print(offsetY - initOffsetY)
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index