2012年5月9日水曜日

マルチタッチイベントサンプル1 - Gesture Recognizer

基本的なところになりますが、iOS Developer Libraryの「iOSイベント処理ガイド」(英語版はEvent Handling Guide for iOS)に沿って、サンプルを公開します。以下のiOSデバイスの基本的な操作について書かれています。
  • マルチタッチイベント
    • 画面にタッチされたなど
  • モーションイベント
    • デバイスが傾けられたなど
  • リモートコントロールイベント
    • 音声を上げるボタンが押されたなど


マルチタッチイベントとは

ユーザが画面タッチすると、マルチタッチイベントが発生して、「指が画面に触れている」「指が動いた」「指が画面から離された」「何本の指で同時にタッチしているか」ということを認識できるようになります。通常、アプリを作成する上で必ず制御しなければならないイベントとなっています。
iOS3.2以前では、タップされた・スワイプされたなどの情報は
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
を使って自前で挙動を定義しなければなりませんでした。しかし、マルチタッチイベントを自分で処理するのは面倒ですし、メンテナンスしづらくなるためバグも発生しやすくなります。
そこで、iOS3.2から用意されている「Gesture Recognizer」クラスを利用します。Gesture Recognizerクラスのおかげで、簡単にマルチタッチイベントを扱えるようになりました。用意されているジェスチャーの種類は限られていますが、頻発するものであるために十分利便性の高いものばかりです。
もし不十分な場合は、自前でGesture Recognizerのサブクラスを作成することになります。

まず今回は、用意されている Gesture Recognizer と、そのサブクラスのサンプルです。

Gesture Recognizerの詳細【iOS 3.2以降】

認識可能なジェスチャーは以下のものが用意されています。
ジェスチャーUIKitのクラス
タップ(タップ数は任意)UITapGestureRecognizer
ピンチインとピンチアウト(ビューのズーム拡大縮小用)UIPinchGestureRecognizer
パニング(ドラッグ)UIPanGestureRecognizer
スワイプ(任意の方向に対応)UISwipeGestureRecognizer
回転(互いに反対の方向への指の移動)UIRotationGestureRecognizer
長押し(「タッチアンドホールド」とも呼ばれる)UILongPressGestureRecognizer

いずれも、ジェスチャーを認識したら、セレクタとして登録してあるメソッドを呼び出してもらうというものです。
例えば、1本指でのシングルタップを認識したい場合は、以下のような具合です。
/* ジェスチャーの登録*/
- (void)addTapGestureRecognizer
{
    UITapGestureRecognizer *singleFingerSingleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleFingerSingleTap:)];
    [self.view addGestureRecognizer:singleFingerSingleTap];
    [singleFingerSingleTap release];
}

/* ジェスチャーが認識された場合に実行される処理 */
- (void)handleSingleFingerSingleTap:(UITapGestureRecognizer *)recognizer
{
    // タップされた場合の処理
}

そして、1本指でのタップの場合は選択し、ダブルタップの場合は拡大したい等の場合は以下のようになります。
- (void)addTapGestureRecognizer
{
    // シングルタップ
    UITapGestureRecognizer *singleFingerSingleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleFingerSingleTap:)];
    // ダブルタップ
    UITapGestureRecognizer *singleFingerDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleFingerDoubleTap:)];
    [singleFingerDoubleTap setNumberOfTapsRequired:2];

    // ダブルタップの失敗時のみシングルタップを実行する (シングルタップとダブルタップが同時に認識されてしまうのを回避する)
    [singleFingerSingleTap requireGestureRecognizerToFail:singleFingerDoubleTap];

    // ビューへの登録とリリース
    [self.allGesturesView addGestureRecognizer:singleFingerSingleTap];
    [singleFingerSingleTap release];
    [self.allGesturesView addGestureRecognizer:singleFingerDoubleTap];
    [singleFingerDoubleTap release];
}

ここでは「requireGestureRecognizerToFailメソッド」を書いていますが、単純にシングルタップとダブルタップのジェスチャーを登録しただけでは、シングルタップとダブルタップの両方が同時に認識されてしまいます。
単純にUIGestureRecognizerを両方を定義した場合
そこでrequireGestureRecognizerToFailメソッドを使うと、ダブルタップの認識に失敗したらシングルタップの処理をするということになります。UIKitのソースコードが見られないので正確には分かりませんが、pdfのサンプルから考えるとダブルタップであると認識されるためには0.3秒以内に二回タップされる必要があり、その間に正しくダブルタップが認識されない場合、シングルタップのハンドラが実行されることになります。
requireGestureRecognizerToFailメソッドでの制御

サンプル

ソースコードはGitHubに公開してあります。
iOS-SampleCodes/GestureRecognizers - GitHub

今回はUIGestureRecognizerのみを使ったサンプルのため非常に簡単ですが、以下、サンプル画像です。
起動すると以下の画面が表示されます。これは3つのビューに分かれています。
  1. 全てのUIGestureRecognizerを追加したビュー
  2. UIPanGestureRecognizerのみを追加したビュー
  3. UISwipeGestureRecognizerのみを追加したビュー
PanとSwipeだけを分けたのは単純に、「1.」のビューで競合してしまい、Panが認識されないからです。全てのジェスチャーを同じビューに追加することはほぼ無いでしょうが、Panは認識されなくなってしまいます。同時に認識させたい場合は「UIGestureRecognizerDelegateプロトコル」の「gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:」を実装することで回避できるのですが、単純にするためビューを分けています。
3つのビューにそれぞれのGestureRecognizerを追加してある

「1.」のビューでPinchGestureを実行「3.」のビューでSwipeGestureを実行

次回

次は、「マルチタッチイベントサンプル2 - カスタムGesture Recognizer」です。カスタムGesture Recognizerの例としてマウスジェスチャー機能を紹介します。

0 コメント:

コメントを投稿