【SwiftUI】タップ/長押しイベントを取得する方法!Gestureメソッドの使い方

この記事からわかること
- Swift UIでタップイベントを取得する方法
- onTapGesture/onLongPressGestureの使用方法
- Gesture(ジェスチャー)とは?
- ジェスチャー構造体の種類
- ジェスチャーの発生位置などの情報を取得するには?
index
[open]
\ アプリをリリースしました /
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
Swift UIでタップや長押しなどのイベントをビューに追加する方法をまとめました。
Gesture(ジェスチャー)とは?
Web制作では「イベント」と呼ばれるクリックやホバーなどのユーザー操作ですがSwiftではGesture(ジェスチャー)と呼ばれ管理されています。
ジェスチャーにはタップや長押し、スワイプ、ピンチなど、スマホを操作する際に発生するイベントが含まれています。
Swift UIに限らず、Swiftではジェスチャーが発生した時に任意の処理を行わせる仕組みが備わっているのです。これは元々イベント処理を持っているボタンなどだけではなく、Textやラベルなど本来イベント処理を保持していないビューに対してもカスタマイズして組み込むことが可能になります。
ジェスチャーに対する処理の組み込みはSwift UIではモディファイア(メソッド)として、UIKitではGestureRecognizer(ジェスチャーレコグナイザー)クラスとして用意されています。しかし両者は別物なので取扱には注意してください。
タップされた時の処理
SwiftUIでビューがタップされた時に任意の処理をさせるにはonTapGesture
モディファイアを使用します。
定義
func onTapGesture(
count: Int = 1,
perform action: @escaping () -> Void
) -> some View
引数:count
引数count
にはタップ回数を指定します。デフォルト値として1
が渡されているので省略すると1回のタップで発火します。
引数:perform
引数perform
にはイベント発生時に実行させたい処理を渡します。
引数の最後が@escaping
のついたクロージャなのでTrailing Closure記法を使って引数名を省略して記述することができます。
使用例
Text("ボタン").onTapGesture {
print("タップされたよ")
}
ダブルタップの検知
ダブルタップを検知して処理を発火させるには引数count
の値を2
にするだけです。
Text("ボタン").onTapGesture(count: 2) {
print("ダブルタップされたよ")
}
引数に渡す数値を変更するだけでタップ数が任意の数行われた場合のみ処理を実行する機能を作ることも可能です。
長押しされた時の処理
ビューを長押しされた時に処理を発火させたい場合はonLongPressGesture
モディファイアを使用します。
定義
func onLongPressGesture(
minimumDuration: Double = 0.5,
perform action: @escaping () -> Void,
onPressingChanged: ((Bool) -> Void)? = nil
) -> some View
引数:minimumDuration
引数minimumDuration
には長押しと判別する時間を浮動小数点数型で指定します。デフォルト値は0.5
となっており、省略可能です。短すぎる時間と指定すると長押しとは言えなくなってしまうので注意してください。
引数:perform
引数perform
には長押しで実行される処理を指定します。
引数:onPressingChanged
引数onPressingChanged
には状態変化時(押された時と離された時)に行うクロージャを指定します。クロージャの引数から現在の状態を真偽値で取得できます。
使用例
Text("ボタン").onLongPressGesture() {
print("長押しされたよ")
}
Swift UIでのモディファイアを使ったジェスチャー管理
Swift UIでのジェスチャー管理はview構造体(プロトコル)のメソッド(モディファイア)として提供されていることがわかったと思いますが実はモディファイアで指定する方法には2種類あります。
1:ジェスチャー専用のモディファイア
1つ目の方法はonTapGesture
やonLongPressGesture
といった各ジェスチャーに対応したView構造体のモディファイアを呼び出す方法です。
Text("ボタン").onTapGesture {
print("タップされたよ")
}
View構造体から呼び出せるメソッドに関しては公式サイトをご確認ください。
2:ジェスチャーモディファイアにジェスチャー構造体を指定する
2つ目はgesture
メソッドを使用する方法です。例えば1つ目と同じタップジェスチャーを取得する場合は以下のようになります。
struct ContentView: View {
let tap = TapGesture()
.onEnded { _ in
print("タップされたよ")
}
var body: some View {
Text("ボタン").gesture(tap)
}
}
gestureメソッドの使い方
gestureメソッドは引数に各ジェスチャー(イベント)型の構造体を渡して使用します。
func gesture<T>(
_ gesture: T,
including mask: GestureMask = .all
) -> some View where T : Gesture
おすすめ記事:【Swift】ジェネリクスの意味と使い方!<T>とは?
引数gesture
にGesture
プロトコルに準拠したジェスチャー構造体を指定します。
ジェスチャー構造体の種類
ジェスチャー構造体 | 概要 |
---|---|
AnyGesture | タイプ消去ジェスチャー? |
DragGesture | ドラッグ |
ExclusiveGesture | 2つのジェスチャを保持しどちらかを実行 |
GestureStateGesture | ジェスチャの状態を更新 |
LongPressGesture | 長押し |
MagnificationGesture | 拡大動作(拡大量を検知) |
RotationGesture | 回転モーション(回転の角度を検知) |
SequenceGesture | 2つのジェスチャを保持し直列に実行 |
SimultaneousGesture | 2つのジェスチャを保持し同時に実行 |
SpatialTapGesture | タップ(場所を検知) |
TapGesture | タップ |
SimultaneousGesture
を使用して画面遷移時に任意の処理を挟む方法を紹介しています。
Gestureプロトコル
各構造体はGestureプロトコルに準拠しており、以下のようなメソッドを保持しています。
associatedtype Value
func onChanged(_ action: @escaping (Self.Value) -> Void) -> _ChangedGesture<Self>
func onEnded(_ action: @escaping (Self.Value) -> Void) -> _EndedGesture<Self>
onChangedメソッド
onChanged
メソッドはジェスチャの値が変化したときに実行する処理を渡せるメソッドです。TapGesture
などでは呼び出すことができませんがDragGesture
などでは使用可能です。
onEndedメソッド
onEnded
メソッドはジェスチャの終了時に実行する処理を渡せるメソッドです。
例えばこの2つのメソッドを組み合わせてビューをスワイプしている間とスワイプ後に指を離したタイミングに処理を組み込むこともできます。
struct ContentView: View {
let drag = DragGesture()
.onChanged { _ in
print("スワイプ中")
}
.onEnded { _ in
print("スワイプ終了")
}
var body: some View {
Text("ボタン").gesture(drag)
}
}
Valueから取得できる情報
Value
にはジェスチャー型ごとに保持する情報が格納されます。引数に渡したジェスチャーによって保持する情報が異なるので注意してください。
struct ContentView: View {
let drag = DragGesture()
.onEnded {value in
dump(value)
}
var body: some View {
Text("ボタン").gesture(drag)
}
}
例えばDragGesture
の場合は以下の通りです。
struct DragGesture.Value {
var location: CGPoint
// ドラッグジェスチャの現在のイベントの場所
var predictedEndLocation: CGPoint
// 現在のドラッグ速度に基づいた、ドラッグが停止した場合の最終的な位置の予測
var predictedEndTranslation: CGSize
// 現在のドラッグ速度に基づいた、ドラッグが停止した場合の最終的な移動の予測
var startLocation: CGPoint
// ドラッグジェスチャの最初のイベントの場所
var time: Date
// ドラッグジェスチャの現在のイベントに関連付けられた時間
var translation: CGSize
// ドラッグジェスチャの開始からドラッグジェスチャの現在のイベントまでの合計変換
}
このようにジェスチャの発生位置や経過時間などがValue
から取得できます。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。
私がSwift UI学習に使用した参考書