【SwiftUI】onTapGestureで座標取得(iOS16以降)※iOS15までの代用方法あり

SwiftUI

iOS16からonTapGetureで座標取得が可能になりました。
非常に便利なのでしっかり確認しておきましょう。

onTapGestureで座標を取得する

クロージャで座標を受け取れる様になりました。
これだけで座標の取得ができます。

@State var location = CGPoint()

.onTapGesture{ location in
    self. location = location
} 

デフォルトはローカル座標です。
グローバル座標を取得したい場合は引数を追加します。
coordinateSpaceという引数が追加されたのでglobalを指定するだけです。

.onTapGesture(coordinateSpace: .global) { location in
    self. location = location
}

当然ですがcountと同時にも使用できます。

SpatialTapGesture

SpatialTapGestureという構造体が追加されています。
onTapGestureでこれを使っているというイメージです。

SpatialTapGesture()
    .onEnded { event in
        self.location = event.location
    }

onEndedを使用します。
クロージャで受け取る値もそのまま座標ではありません。
メンバのlocationが持っているのでそちらから座標を取得してください。

別な場所に定義しておいてgesture modifierに渡す場合はこちらを使用します。

var gesture:some Gesture {
    SpatialTapGesture().onEnded { event in
        self.location = event.location
    }
}

//gestureに渡す
.gesture(gesture)

iOS15までの場合は

SpatialTapGestureがありません、onTapGestureもこれを使っているはずなので当然未対応です。
しかしDragGestureには元から座標取得がありました。こちらを使って代用が可能です。

@State var point = CGPoint()

//
DragGesture(minimumDistance: 0)
    .onEnded{ event in
        self.location = event.location
        //self.location = event.startLocation //開始位置も取れる
    }

//gestureに入れるとこんな感じ
.gesture(DragGesture(minimumDistance: 0).onEnded{ event in
    self.location = event.location
})

ポイントはminimumDistanceです。これを指定するとタップで反応する様になります。
また、手を離した位置を取得しています。
startLocationで開始位置も取得できますが、SpatialTapGestureはonEndedなので合わせました。

ただし、このままでは若干挙動が異なります。Dragしても反応してしまうからです。
Drag時は反応しない様に閾値を決めて、移動していない時のみ取得しましょう。

DragGesture(minimumDistance: 0)
    .onEnded{ event in
        if event.translation.width < 1 && event.translation.height < 1 { //実際はもっと大きい
             location = event.location
        }
    }

DragGesture(minimumDistance: 0)
    .onEnded{ event in
        if point.location == event.startLocation { //移動が全くない場合
             location = event.location
        }
    }

閾値は実際のSpatialTapGestureは結構動いても反応します。
時間で見ているのか距離で見ているのはわかりませんが10以上は動いても反応します。

ロングタップでも反応しますが、SpatialTapGestureもロングタップでもある程度反応するため、
おおよそ同じ挙動になったと考えてよいでしょう。

カウントに関しては考慮しません。
工夫する事で実現可能だとは思いますが今回はonTapGestureとSpatialTapGestureがメインなので、
そこまでは作っていません。

最後に

iOS16からSpatialTapGestureが実装されonTapGestureで座標取得が可能になりました。
以前より簡単に分かりやすく実装できる様になり、
カウントも使用できる為、ダブルタップやトリプルタップにも対応できます。

modifierで済み簡単に分岐できるので、互換を切らずiOS16ユーザー向けに追加も簡単だと思います。
ぜひ使ってみてください。

コメント

タイトルとURLをコピーしました