93
82

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

よくわかるUIGestureRecognizerDelegate

Last updated at Posted at 2017-03-04

はじめに

カスタムで作ったUIGestureRecognizerの認識を制御したり、他のジェスチャーとの同時認識や失敗の制御をしたいことは多々あると思います。
その際に、便利なのがUIGestureRecognizerDelegateです。
色々なメソッドが提供されていますが、日本語の資料があまり多くないので、どれがどういった時に役に立つかをまとめてみました。

UIGestureRecognizerDelegate

UIGestureRecognizerDelegateはアプリのジェスチャー認識の振る舞いを微調整するためのprotocolです。
UIKitのUIGestureRecognizerDelegateには以下のメソッドが提供されています。
すべてoptionalです。

UIKitから抜粋
public protocol UIGestureRecognizerDelegate : NSObjectProtocol {
    // Gestureの認識を開始させるかの制御
    @available(iOS 3.2, *)
    optional public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool

    // Gestureの同時認識を許可するかの制御
    @available(iOS 3.2, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool

    // 自身のGestureまたは他方のGestureを失敗させるかの制御
    @available(iOS 7.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool
    @available(iOS 7.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool

    // GestureのTouchを受け取るかの制御
    @available(iOS 3.2, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

    // GestureのPressを受け取るかの制御
    @available(iOS 9.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive press: UIPress) -> Bool
}

使い方

使い方は簡単で、UIGestureRecognizerに対してプロパティのdelegateに受け取りたい先のインスタンス(基本的にVCになると思います)をセットし、UIGestureRecognizerDelegateを実装するだけです。例を示します。

ViewController.swift
class ViewController: UIViewController {
    var gesture: UIGestureRecognizer?
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // gestureの初期化処理を行う

        // delegateのセット
        gesture?.delegate = self
    }
}

extension ViewController: UIGestureRecognizerDelegate {
    // ここに必要なdelegateメソッドを実装していく
}

それぞれのdelegateメソッドの詳細

ここからは、それぞれのdelegateメソッドの使い方を以下の3つの制御分類に分けて、例とともに紹介していきます。

  • 認識制御系メソッド
  • 同時認識制御系メソッド
  • 失敗制御系メソッド

認識制御系メソッド

gestureRecognizerShouldBeginメソッド

Gestureの認識を開始させるかの制御を行いたいときは、gestureRecognizerShouldBegin()メソッドを実装します。
UIGestureRecognizerDelegateのメソッドの中でも一番使用用途が多いかもしれません。
UIGestureRecognizerはstateプロパティを持っており、.possible, .began, .changed, .ended, .cancelled, .failedといった具合に認識状況に応じて変化していきます。
このstateでいうところの、.possibleから.beganになるべきか.failedになるべきかをこのメソッドで制御できます。
このメソッドを実装しない場合は、デフォルトでtrueが返ります。
例えば、特定のgestureのみを制御させたいときは、下記のように使えます。

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer === self.gesture { // 特定のgestureを捕捉して認識させるか制御
        // hogehoge
    }
    return true
}

shouldReceive touchメソッド

touchesBegan()より前に呼ばれるメソッドで、touchを受け取るかどうかを制御します。
trueを返すと、その後touchesBegan()メソッドが呼ばれます。falseの場合は呼ばれません。
このviewからのtouchは受け取らないといった制御を入れたい時に使うことができます。

shouldReceive pressメソッド

iOS9から利用可能になったメソッドです。
pressesBegan()より前に呼ばれるメソッドで、pressを受け取るかどうかを制御します。
trueを返すと、その後pressesBegan()メソッドが呼ばれます。falseの場合は呼ばれません。
このviewからのtouchは受け取らないといった制御を入れたい時に使うことができます。

同時認識制御系メソッド

shouldRecognizeSimultaneouslyWithメソッド

このメソッドは、複数のGestureを同時認識させたい時に使います。
例えば、VCが持つviewの上に貼ったUIScrollView系のViewがいる場合を考えます。
認識の優先度的には、最前面のGestureが認識されるので、ScrollViewの中に設定されているGestureの方が優先されてしまい、VCが持つviewにaddしたカスタムGestureはこのままでは、認識できません。
そこで、このメソッドを用いて、同時認識を許可してあげることによって、カスタムのGestureも認識させることができるようになります。

使い方は簡単で、同時認識させたい場合は、trueを返してあげればよいです。
逆に同時認識をさせたくない場合はfalseを返しましょう。このメソッドを実装しない場合はデフォルトでfalseが返ります。
このメソッドでは、競合するGestureをotherGestureRecognizerで取得できるので、そのGestureのクラスや親のViewのクラスによって制御することもできます。
下記に例を示します。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer === self.gesture { // 特定のgestureのとき
        // hogehoge
    }

    if otherGestureRecognizer is UIPanGestureRecognizer { // 他方がPan(ドラッグ)Gestureのとき
        // hogehoge
    }

    if otherGestureRecognizer.view is UIScrollView { // 他方のGestureの親がUIScrollViewのとき
        // hogehoge
    }
    return false
}

失敗制御系メソッド

これらは、2つのGestureの認識が競合した時に自身が失敗をするか/他方の失敗をさせるか制御するメソッドです。
どっちがどっちだっけ?と混同しやすいので注意が必要です。

shouldRequireFailureOfメソッド

このメソッドは、delegateをセットしたGesture自身が他のGestureによって失敗されるかどうかを制御します。
もう一つの失敗制御メソッドのshouldBeRequiredToFailByと混同しやすいので注意してください。
このメソッドの返り値をtrueにすることで、他のGestureによって自身のGestureが失敗させられることを許可します。
もちろん、他のGestureが失敗を要求しない限りは自身のGestureが失敗させられることはありません。
このメソッドを実装していない場合は、デフォルトではfalseが返るため、他のGestureによって失敗させられることはありません。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    // trueなら他のGestureによって失敗要求が出された場合、失敗する
    return true
}

shouldBeRequiredToFailByメソッド

上に対して、こちらはGesture自身が他のGestureに対して失敗を要求するかどうかを制御します。
一見、英語からすると意味が逆なのではと思う方もいると思いますが、これで正しいです。
このメソッドの返り値をtrueにすることで、ほかのGestureは失敗が要求されます。
注意が必要なのは、他のGestureがshouldRequireFailureOfメソッドによって、失敗されないように制御されていた(falseを返していた)場合、ここでtrueを返しても他のGestureが失敗されることはありません。
このメソッドを実装していない場合は、デフォルトではfalseが返るため、他のGestureに失敗の要求をすることはありません。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    // trueなら他のすべての競合するGestureに対して失敗要求を出す
    return true
}

Tips: UIKitのクラスで使われているGestureはdelegateをセットできない

カスタムで作ったGestureRecognizerではなく、UIKitの中で使われているアクセス可能なGestureのdelegateも実装したいと考える方もいると思います。
が、これらのGestureのdelegateをセットした場合、エラーが出てクラッシュしてしまうので注意が必要です。

下記は、UITableView(UIScrollView)のpanGestureRecognizerのdelegateをセットした際に実行した時のエラーログです。
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UIScrollView's built-in pan gesture recognizer must have its scroll view as its delegate.'

まとめ

UIGestureRecognizerDelegateを正しく理解して、ユーザにとって使いやすい、適切なジェスチャーハンドリングを実装しましょう。

参考リンク

93
82
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
93
82

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?