Gesture Recognizer — 概要、iOSのタッチ処理

著者: IT Sectr 公開日: 2026-02-27 読了時間: 8 分
Gesture RecognizerはUIKitの抽象化であり、ユーザーのタッチシーケンスを認識されたジェスチャー(タップ、スワイプ、長押し、ピンチ、ローテーション)に変換します。touchesBegan/touchesMovedメソッドで座標を手動で追跡する代わりに、開発者はUIViewに既製のUIGestureRecognizerオブジェクトを追加し、アクションメソッドを実装します。Apple Developer Documentationによると、UIGestureRecognizerは7つの組み込みサブクラスをサポートし、モバイルアプリケーションの95%の標準的なジェスチャーをカバーしています。

重要ポイント

  • UIGestureRecognizerはタッチパターン(タップ、スワイプ、ピンチ、ローテーション、長押し)を認識するためのUIKitの抽象クラスです。
  • 7つの組み込みサブクラス:UITapGestureRecognizer、UISwipeGestureRecognizer、UIPinchGestureRecognizer、UIRotationGestureRecognizer、UILongPressGestureRecognizer、UIPanGestureRecognizer、UIScreenEdgePanGestureRecognizer。
  • 各レコグナイザーには状態(possible、began、changed、ended、cancelled、failed)があり、指の動きに応じて更新されます。
  • ジェスチャー間の競合を解決するには、require(toFail:)メソッドを使用して認識の優先順位を設定します。
  • Gesture RecognizerはSwiftUIでUIKitおよびUIViewRepresentableと連携しますが、ネイティブSwiftUIではジェスチャーモディファイアが使用されます。

Gesture Recognizerとは?

UIGestureRecognizerはUIKitの抽象基底クラスで、ジェスチャー認識プロセスを個別のオブジェクトに分割します。各サブクラスは1つのジェスチャータイプを処理します。UITapGestureRecognizerは特定のタッチ数のタップを処理し、UISwipeGestureRecognizerは指定された方向のスワイプを処理します。開発者はaddGestureRecognizer(:)メソッドを介してUIViewにレコグナイザーを追加し、UIKitが自動的にタッチを追跡し、状態を更新し、認識時にアクションを呼び出します。

UIGestureRecognizer(iOS 3.2、2010年)以前は、開発者はUIResponderメソッド(touchesBegan、touchesMoved、touchesEnded)をオーバーライドし、タッチの軌跡を手動で分析していました。これによりコードの重複と同時タッチ処理時のエラーが発生していました。AppleはこのロジックをUIGestureRecognizerにカプセル化し、マルチタッチ、ジェスチャーのキャンセル、1つのビューでの複数レコグナイザーの同時動作をサポートしました。

WWDCセッションによると、Gesture RecognizerはiPadで最大11の同時タッチ、iPhoneで5つの同時タッチを処理します。IT Sectrでは、すべてのUIKitプロジェクトでユーザー入力処理の標準方法としてUIGestureRecognizerを使用しています。これにより手動のtouchesBegan追跡に関連するバグが排除されました。

UIGestureRecognizerの状態

各UIGestureRecognizerは7つの可能な状態を経由します。これらはUIGestureRecognizer.State列挙型で定義されています。これらの状態は、タッチ検出から完了またはキャンセルまでの認識ライフサイクルを反映します。状態を理解することは、カスタムレコグナイザーの実装と競合のデバッグに不可欠です。

状態 意味 発生タイミング
.possible 初期状態、ジェスチャーはまだ認識されていない ビューに追加された直後
.began ジェスチャーが認識され実行を開始した pan/longPressで最初の指の動き時
.changed ジェスチャーのパラメータが変更(座標、角度) 指が動くたび
.ended ユーザーが指を離し、ジェスチャー完了 touchesEnded時
.cancelled システムによりジェスチャーが中断(着信、画面回転など) touchesCancelled時
.failed 条件に基づきジェスチャーが認識されなかった 認識なしのtouchesCancelled時
.recognized .endedの同義語、ジェスチャーが正常に認識された .endedと同じ

離散ジェスチャー(タップ、スワイプ)は.possibleから直接.endedまたは.failedに移行します。連続ジェスチャー(パン、ピンチ、ローテーション、長押し)は.possible → .began → .changed(繰り返し)→ .endedを経由します。アクションメソッドでgestureRecognizer.stateを確認することで、ジェスチャーの開始、変更、終了を区別できます。

iOSのジェスチャータイプ

UIKitは7つの組み込みUIGestureRecognizerサブクラスを提供し、ほとんどのインタラクションシナリオをカバーしています。各サブクラスには特定の設定があります:タップのnumberOfTapsRequired、スワイプのdirection、長押しのminimumPressDurationなどです。

  • UITapGestureRecognizer — シングルおよびマルチタップを認識します。タップ数(numberOfTapsRequired)と指の数(numberOfTouchesRequired)を設定可能。ボタン、リンク、要素選択に使用されます。
  • UISwipeGestureRecognizer — 4方向(right、left、up、down)のいずれかのスワイプを認識します。directionプロパティが方向を設定し、numberOfTouchesRequiredが指の数を設定します。
  • UIPanGestureRecognizer — ドラッグ用の連続ジェスチャー。translation(in:)メソッドは開始点からのオフセットを返します。ドラッグ&ドロップ、カルーセル、プル・トゥ・リフレッシュに使用されます。
  • UIPinchGestureRecognizer — 2本指でのスケーリング。scaleプロパティは現在のスケール係数を反映します。Pinch-to-Zoomの記事で詳しく説明されています。
  • UIRotationGestureRecognizer — 2本指での回転。rotationプロパティはラジアン単位の角度を保存します。画像、マップ、キャンバスの回転に使用されます。
  • UILongPressGestureRecognizer — 長押し。パラメータ:minimumPressDuration(秒)、allowableMovement(ピクセル)。コンテキストメニュー、要素のドラッグに使用されます。
  • UIScreenEdgePanGestureRecognizer — 画面端からのパン。edgesプロパティが追跡する端を指定します。ナビゲーションジェスチャー、通知パネルに使用されます。

カスタムジェスチャー(例えばジグザグ描画)の場合は、touchesBegan、touchesMoved、touchesEndedをオーバーライドし状態を更新するUIGestureRecognizerのサブクラスを作成します。Appleは可能な限り組み込みクラスを使用することを推奨しています。これらは最適化されており、相互に正しく連携します。

ジェスチャー間の競合解決

1つのUIViewで複数のUIGestureRecognizerを使用する場合(タップとダブルタップなど)、認識競合が発生します。ダブルタップ時にシングルタップが先に発火します。Appleはrequire(toFail:)メソッドを提供し、一方のジェスチャーの認識を他方が失敗するまで遅延させます。

このメカニズムは次のように機能します。tapRecognizer.require(toFail: doubleTapRecognizer)を呼び出すことで、doubleTapRecognizerが.failedで終了した後にのみtapRecognizerが.recognized状態に移行するよう指定します。これによりシングルタップの実行前に約0.3秒の遅延が追加されます。ユーザーが2回タップすると最初のタップは無視されます。代替アプローチとして、UIGestureRecognizerDelegateのgestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)メソッドを使用すると、同時認識が可能になります(例:マップのpan + pinch)。

WWDC 2020によると、UIKitアプリケーションのバグの約15%がジェスチャー競合の不適切な設定に関連しています。IT Sectrではアプローチを標準化しました。各画面にrequire(toFail:)の優先順位を持つジェスチャースキームを設定し、ダブルタップ発火のバグを完全に排除しました。

Swiftコード例

例1:UITapGestureRecognizer — 画像のタップ

UIImageViewにシングルタップハンドラーを追加します。タップすると画像の不透明度が変化します。ビューへのジェスチャーバインディングを示す簡単な例です。

swift
import UIKit

class ImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let tap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleTap(_:))
        )
        tap.numberOfTapsRequired = 1
        imageView.addGestureRecognizer(tap)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handleTap(_: UITapGestureRecognizer) {
        UIView.animate(withDuration: 0.2) {
            self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
        }
    }
}

重要なポイント:UIImageViewのisUserInteractionEnabledはデフォルトでfalseです。このフラグがないと、Gesture Recognizerはタッチを受け取りません。UIViewとUIButtonではこのフラグはデフォルトで有効です。アクションメソッドはUITapGestureRecognizerパラメーターを受け取り、location(in:)でタップ座標を取得できます。

例2:UISwipeGestureRecognizer — ナビゲーション用スワイプ

左スワイプで前の画面に戻る機能を実装します。方向設定とコントローラーのルートビューへのジェスチャーバインディングを示します。

swift
import UIKit

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swipeLeft = UISwipeGestureRecognizer(
            target: self,
            action: #selector(handleSwipe(_:))
        )
        swipeLeft.direction = .left
        view.addGestureRecognizer(swipeLeft)
    }

    @objc private func handleSwipe(_: UISwipeGestureRecognizer) {
        navigationController?.popViewController(animated: true)
    }
}

UISwipeGestureRecognizerは離散ジェスチャーです。認識後すぐに.recognizedに移行し、中間の.changed状態はありません。したがって、アクションで状態を確認する必要はありません。ジェスチャーが認識されるか(アクションが発火)、認識されないかのいずれかです。directionプロパティは.left、.right、.up、.downの4つの値のいずれかを受け入れます。複数の方向をサポートするには、方向ごとに個別のレコグナイザーを作成します。

例3:require(toFail:)を使用したジェスチャーの組み合わせ

競合なく1つのビューでシングルタップとダブルタップを設定する方法を示します。ダブルタップレコグナイザーに優先度があり、シングルタップはダブルタップが認識されない場合にのみ発火します。

swift
import UIKit

class TapViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let singleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleSingleTap)
        )
        singleTap.numberOfTapsRequired = 1

        let doubleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleDoubleTap)
        )
        doubleTap.numberOfTapsRequired = 2

        singleTap.require(toFail: doubleTap)

        view.addGestureRecognizer(singleTap)
        view.addGestureRecognizer(doubleTap)
    }

    @objc private func handleSingleTap() {
        print("Single tap — after 0.3s delay")
    }

    @objc private func handleDoubleTap() {
        print("Double tap — instant")
    }
}

require(toFail:)がない場合、ダブルタップ時にhandleSingleTapが最初に発火し、次にhandleDoubleTapが発火します。これによりUXが損なわれます。require(toFail:)を使用すると、シングルタップは約0.3秒待機して2回目のタップが来ないことを確認します。IT Sectrでは、このパターンが画像エディターやギャラリーで使用されており、ダブルタップでズーム、シングルタップで要素選択を行います。

よくある質問

1つのビューで複数のGesture Recognizerを使用できますか?

はい、UIViewは複数のUIGestureRecognizerインスタンスを同時にサポートします。競合を解決するには、認識順序を設定するrequire(toFail:)メソッドを使用します。ジェスチャーの並行操作(例:マップのpan + pinch)には、trueを返すgestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)デリゲートメソッドを実装します。

UIGestureRecognizerとtouchesBeganの違いは?

UIGestureRecognizerはタッチパターンを自動認識し状態を管理する高レベル抽象化です。touchesBeganはUIResponderの低レベルメソッドで、手動による座標追跡、タイミング、タッチキャンセルが必要です。Gesture Recognizerはシンプルで信頼性が高く、標準ジェスチャーに適しています。touchesBeganはカスタムグラフィックスにのみ使用すべきです。

Gesture RecognizerはSwiftUIで動作しますか?

ネイティブSwiftUIではモディファイア(onTapGesture、onLongPressGesture、DragGesture、MagnificationGesture、RotationGesture)を使用します。これらはSwiftUI階層に統合されたUIGestureRecognizerの宣言的アナログです。必要に応じてUIViewRepresentableを介してUIKitレコグナイザーをラップできますが、AppleはネイティブSwiftUIジェスチャーの使用を推奨しています。

まとめ

  • UIGestureRecognizerは自動状態管理によりジェスチャー認識ロジックを個別オブジェクトに分解するUIKitの抽象化です。
  • 7つの組み込みサブクラスが標準ジェスチャーの95%(タップ、スワイプ、パン、ピンチ、ローテーション、長押し、エッジスワイプ)をカバーします。
  • 各レコグナイザーは.possibleから.ended/.failedまでの7つの状態を経由し、離散および連続ジェスチャーの正確な追跡を可能にします。
  • require(toFail:)メソッドが認識優先順位を設定することで、1つのビューでのジェスチャー間の競合を解決します。
  • SwiftUIは宣言的ジェスチャーモディファイアを使用しますが、UIKitレコグナイザーはUIViewRepresentableを介して利用可能です。
  • Gesture RecognizerはUIKitプロジェクトで必須です。touchesBeganはカスタム描画と低レベルグラフィックスにのみ使用すべきです。
  • タップ/ダブルタップペアにはrequire(toFail:)を、並行ジェスチャーにはgestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)を使用します。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください