競合するUIGestureRecognizerを上手く共存させる

viewに割り当てたジェスチャー同士がバッティングした時にはどれを失敗させるのか、事前に決めておく必要があります。

こういった処理は通常、画面上のジェスチャーを同列に扱えるViewControllerの中で書く訳ですが、複雑なアプリになると出来るだけVCには書きたくなくなります。

そこで、ある程度良く使うダブルタップ縦横のドラッグだけは単独で完結するサブクラスにしておいてStoryboardで使うと便利になります。あとはIBAction書くだけ。

シングルタップと共存できるDoubleTapGestureRecognizer

タップ回数2はStoryboardで設定します。

class DoubleTapGestureRecognizer: UITapGestureRecognizer, UIGestureRecognizerDelegate {

    override func awakeFromNib() {
        super.awakeFromNib()
        delegate = self
    }

    // ダブルの時にシングルを失敗させる
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

シングル、ダブルに加えてトリプルも共存させる場合はもうちょい工夫が必要ですが、シングルの判定が遅れるので滅多に使わないと思います。

縦スワイプと共存できる横ドラッグHorizontalPanGestureRecognizer

縦スクロールのテーブルに割り当てると干渉せずに横ドラッグだけを検出できます。(セルのスライドメニューが無効な時)

class HorizontalPanGestureRecognizer: UIPanGestureRecognizer, UIGestureRecognizerDelegate {

    override func awakeFromNib() {
        super.awakeFromNib()
        delegate = self
    }

    // 水平方向のみ有効にする
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        let pan = gestureRecognizer as! UIPanGestureRecognizer
        let trans = pan.translation(in: gestureRecognizer.view)
        return fabs(trans.x) > fabs(trans.y)
    }
}

横スワイプと共存できる縦ドラッグVerticalPanGestureRecognizer

class VerticalPanGestureRecognizer: UIPanGestureRecognizer, UIGestureRecognizerDelegate {

    override func awakeFromNib() {
        super.awakeFromNib()
        delegate = self
    }

    // 垂直方向のみ有効にする
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        let pan = gestureRecognizer as! UIPanGestureRecognizer
        let trans = pan.translation(in: gestureRecognizer.view)
        return fabs(trans.x) < fabs(trans.y)
    }
}

もっと複雑なジェスチャーを混在させる

お互いに相手のジェスチャーを失敗させる設定にしていた場合でも問題が起こらない様に工夫しなくてはなりません。