【iOS】UITapGestureの共存


iOS その3 Advent Calendar

5日目を書かせて頂きます!

を書かせてもらっています!

では本題に入ります。

1つのViewでシングルタップとダブルタップを検知したい

ってことが前にありました。

普通にかくと以下のようになると思います。

ViewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleSingleTapGesture(_:)))
        singleTapGesture.numberOfTapsRequired = 1
        singleTapGesture.numberOfTouchesRequired = 1
        view.addGestureRecognizer(singleTapGesture)

        let doubleTapGesture = UITapGestureRecognizer(target: self , action: #selector(ViewController.handleDoubleTapGesture(_:)))
        doubleTapGesture.numberOfTapsRequired = 2
        doubleTapGesture.numberOfTouchesRequired = 1
        view.addGestureRecognizer(doubleTapGesture)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // single tap
    @IBAction func handleSingleTapGesture(_ gesture: UITapGestureRecognizer) {
        print("single tap")
    }

    @IBAction func handleDoubleTapGesture(_ gesture: UITapGestureRecognizer) {
        print("double tap")
    }

}

ただ上記のように実装すると以下のようにログが流れます。

わかりづらいかもですが、doubleTapGestureのイベントの前にも必ずsingleTapGestureの処理が入っていることです。

望んでいるのはdoubleTapGestureが動作するときはsingleTapGestureは反応して欲しくないのです。

ということで手を加える

以下のように実装すると僕が望んだ通りの処理になりました。

ViewController.swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleSingleTapGesture(_:)))
        singleTapGesture.numberOfTapsRequired = 1
        singleTapGesture.numberOfTouchesRequired = 1
        view.addGestureRecognizer(singleTapGesture)

        let doubleTapGesture = UITapGestureRecognizer(target: self , action: #selector(ViewController.handleDoubleTapGesture(_:)))
        doubleTapGesture.numberOfTapsRequired = 2
        doubleTapGesture.numberOfTouchesRequired = 1
        view.addGestureRecognizer(doubleTapGesture)

        // こいつを加える!!
        singleTapGesture.require(toFail: doubleTapGesture)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // single tap
    @IBAction func handleSingleTapGesture(_ gesture: UITapGestureRecognizer) {
        print("single tap")
    }

    @IBAction func handleDoubleTapGesture(_ gesture: UITapGestureRecognizer) {
        print("double tap")
    }

}

すると以下のようになります。

ログが重複しなくりました!

懸念点

今回加えたUIGestureRecognizerfunc require(toFail otherGestureRecognizer: UIGestureRecognizer)は以下のように説明がありました。

Creates a dependency relationship between the receiver and another gesture recognizer when the objects are created.

require(toFail:) - UIGestureRecognizer | Apple Developer Documentationより

最近とても優秀なGoogle翻訳にかけると

受信者と別のジェスチャ認識プログラムがオブジェクトの作成時に依存関係を作成します。

となりました。

簡単にソースコードの意味で説明すると

singleTapGesture.require(toFail: doubleTapGesture)doubleTapGestureFailしたらsingleTapGestureが実行されるという依存関係を結んだということになりますかね...。

最近betaツールでスクショを送れたりページにコメントできたりというのがあって、3本指でタップしたらなどのイベントを生成していると思いますが、かぶることもあり得るのでここら辺上記のメソッドで依存関係を一番最後に回すようなことを考えたら楽になりそうだなって思ったり...。

最後に

GIPHY CAPTUREとっても便利でした。

以上です。