[SwiftUI]Gesture Stateやupdatingとか

2022年2月21日

Appleの公式をDeepLで翻訳しつつ、ちょっとわかり辛いところは内容を補完してます。

公式の内容

ユーザーがジェスチャーを行っている間、プロパティを更新し、ジェスチャーが終了するとプロパティを初期状態に戻すプロパティラッパータイプです。

@propertyWrapper @frozen struct GestureState<Value>

プロパティを @GestureState として宣言し、ジェスチャーの update(_:body:) コールバックのパラメータとしてそのバインディングを渡し、その更新を受け取ります。GestureState として宣言されたプロパティは、ジェスチャが非アクティブになると暗黙的にリセットされるので、過渡的な状態を追跡するのに適しています。

プロパティを @GestureState として宣言することで、長押しジェスチャを Circle に追加し、ジェスチャ中にインターフェースを更新することができます。

struct SimpleLongPressGestureView: View {
    @GestureState var isDetectingLongPress = false

    var longPress: some Gesture {
        LongPressGesture(minimumDuration: 3)
            .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                gestureState = currentState
            }
    }

    var body: some View {
        Circle()
            .fill(self.isDetectingLongPress ? Color.red : Color.green)
            .frame(width: 100, height: 100, alignment: .center)
            .gesture(longPress)
    }
}

何もしていない時は、円が緑色です。

タップすると縁が赤色になります。タップした状態を維持していても3秒経つとまた緑に戻ります。

updatingについて

定義は以下

@inlinable public func updating<State>(_ state: GestureState<State>, body: @escaping (Self.Value, inout State, inout Transaction) -> Void) -> GestureStateGesture<Self, State>

ジェスチャーの値の変更に伴い、提供されたジェスチャーの状態プロパティを更新します。
このコールバックを使用して、一時的な UI 状態を更新します。
パラメータ
state – ビューの GestureState プロパティへのバインディング。
body – ジェスチャの値が変更されたときに SwiftUI が呼び出すコールバック。その currentState パラメータは、更新されたジェスチャーを作成します。gestureStateパラメータは、以前のジェスチャーの状態を表します。 transactionはジェスチャーのコンテキストを表します。

戻り値
元のジェスチャーの値が変わると提供された状態を更新し、ユーザーがジェスチャーをキャンセルまたは終了すると状態を初期値にリセットします。

内容の補完と理解

これ実際にソースコードをコピペして動かしてみないと多少誤解しそうだ。
実際に動かしてみて分かったこと

・minimumDurationは秒で、タップしてから3秒で円の色は変化する。

・updating処理はタップした瞬間に発生する。持続的に呼ばれるわけではなく、タップした一度のみ。(一度のみと言ってますが、リセット後にもう一度タップすると呼ばれた。)

gestureStateとcurrentStateの型はGestureのタイプによって変化する。

gestureState = currentStateをコメントアウトすると色が変わらなくなる。

updating処理でブレーク止めてみると、それぞれ以下のような値を保持していることがわかった。
ただ、transactionはジェスチャーのコンテキストが入ってくるとのことでしたが、空の配列を保持しているだけでした。

MagnificationGestureのサンプル

こちらのサンプルでもGestureStateが使われていて、使い方としては似ている。
gestureState = currentStateも同じだ。transactionはどのタイミングで使うのかはよくわからない。

struct MagnificationGestureView: View {

    @GestureState var magnifyBy = 1.0

    var magnification: some Gesture {
        MagnificationGesture()
            .updating($magnifyBy) { currentState, gestureState, transaction in
                gestureState = currentState
            }
    }

    var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .scaleEffect(magnifyBy)
            .gesture(magnification)
    }
}

実際にコードを実行してみると、gestureStategestureStateとcurrentStateが Dobuleになっていた。
イベントごとにgestureStateの型は変化するようです。

SwiftUI

Posted by takumioda