[iOS][Xcode 4.2] シングルタップ、ダブルタップの区別とロングタップ(長押し)

 UIButtonに対してシングルタップ、ダブルタップを割り付けるのは骨が折れる。いや、そもそもできるのかよくわからない。
 出来るのは出来ると思うんですよ。Touch up Inside を一定時間に何回検出したとかしないとかすればいいわけで。

 でも、酷く面倒なので、というか、こういう場合は UIView(またはそのサブクラス)のUIGestureRecognizerを使うって事で。

 

// UITapGestureRecognizer のインスタンス生成。targetとしてselfを渡して、actionに動かしたいメソッドを指定する
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapMethod:)];

// 検出したい UIView のサブクラスにGestureをaddする
[view addGestureRecognizer:tapGesture];

// ちゃんとreleaseしておきます
[tapGesture release];

.....

// tap して呼び出すメソッドね
- (void)tapMethod:(id)sender{ ... }

 

 みたいな感じでやるとaddGestureRecognizerでUITapGestureRecognizerをaddしたviewをタップした時にメソッドが呼び出されます。
 ほいじゃさ、ダブルタップはどうしましょう?

 ダブルタップを検出したいときは、addGestureRecognizer で add する前に

[tapGesture setNumberOfTapsRequired:2];

  と、UITapGestureRecognizer のインスタンス numberOfTapsRequired に回数を設定してやります。

 なので、シングルタップとダブルタップを検出したければUITapGestureRecognizerのインスタンスを二つ作って、それぞれnumberOfTapsRequired に指定する回数を変えてやればいいわけです。

// UITapGestureRecognizer のインスタンスを二つ生成。
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapMethod:)];

// singleの方はtap1回で反応するようにする
[singleTapGesture setNumberOfTaps:1];

UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapMethod:)];

// doubleの方は当然2回だわ [doubleTapGesture setNumberOfTaps:2];

 
// 検出したい UIView のサブクラスにGestureをaddする
[view addGestureRecognizer:singleTapGesture];
[view addGestureRecognizer:secondTapGesture];

// ちゃんとreleaseしておきます
[tapGesture release];

 で…まあ、実行するとすぐに分かるのですが、どうやってもシングルとダブルを同時に検出するんですよ(笑)
 一回タップのときは当然シングルしか検出しないんですけど、2回タップした場合、シングルタップとダブルタップを検出します。

 これを防止する為、一回タップのインスタンスをビューに addGestureRecognizer する前に

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];

 

 と、singleTapGesture 失敗時は doubleTapGestureを実行しますよ、と設定します。
 こうすることで、シングルタップとダブルタップの区別が出来るようになります。ちょっとシングルタップの反応が鈍くなるんですけど…まあ、それはまた考えようかな…

 

 長押しもほとんど同じ機構で検出できます。使うのが UITapGestureRecognizer のインスタンスじゃなく、UILongPressGestureRecognizer であるって事だけですね。

 

// UILongPressGestureRecognizer のインスタンス生成。targetとしてselfを渡して、actionに動かしたいメソッドを指定する
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMethod:)];

// 検出したい UIView のサブクラスにGestureをaddする
[view addGestureRecognizer:longPressGesture];

// ちゃんとreleaseしておきます
[longPressGesture release];

.....

  ところがまあ、これもちょっと問題があるんです。
 確かにこれで長押しが検出できるんですが、長押し検出時と、タップ解除(指を離した時)の2回、メソッドが呼び出されてしまうのです。

 二回呼び出されてしまうのは仕方ないので、メソッドの側でガードを掛けます。

// 長押しで呼び出すメソッド
- (void)longPressMethod:(id)sender{

    if([sender state] == UIGestureRecognizerStateBegan){
        // 長押し検出時の処理
    }else if([sender state] == UIGestureRecognizerStateEnded){
        // 指を離したときの処理
    }
}

 と、するとですね、少なくとも長押し検出時と指を離したときで違う処理が動くみたいです。

 いつもの事なんですが…これ、正しくないやり方かもしれないです。でもまあ、動くのでとりあえずはいいかな、と。
 もっと正しいやり方ご存知の方いらっしゃいましたら教えて下さいね。