[iPhoneアプリ開発] UIImageViewでタッチとホールドを検出


2012/2/14 コードの部分を修正しました。

iPhoneアプリ開発で画像をタッチしたりホールドしたらアクションをおこしたい・・
って場合はあると思います。
そのメモです。ざっくりです。

コーディングのポイント

タッチとホールドを実現するのにキーとなるコードは以下の通り。
これだけわかれば組めるはず。

UIImageViewのタッチを有効にします。
(self = UIImageView)

// タッチイベントを許可する
self.userInteractionEnabled = YES;
// マルチタッチを有効にする。
[self setMultipleTouchEnabled:YES];

すると、以下の3つのタッチイベントが有効になります。

// タッチした時に呼ばれるアクション
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
// タッチ離した時に呼ばれるアクション
– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

ホールドはNSTimerを使って実現します。

[NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(touchesHold:withEvent:) userInfo:nil repeats:NO];

実際のコード

妖精の画像をタッチしたりホールドするとアクションがおこしたかった。
妖精をUIImageViewを継承してFairyクラスとしてます。

#import <Foundation/Foundation.h>

@interface TestImageView : UIImageView {
    NSTimer *touchTimer;   
}

@end

@implementation TestImageView

- (void)viewDidLoad
{
    [super viewDidLoad];
    //タッチイベントを許可する
  self.userInteractionEnabled = YES;
  [self setMultipleTouchEnabled:YES];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"タッチイベント開始");
    
    /**
     * NSTimerでホールド処理を呼び出す
     * ホールド中に処理を繰り返すならrepeatsをYES、そうでなければNO
     * ホールドの座標を使用したい場合はUserInfoにtouchesを渡す
     */
    touchTimer = [NSTimer scheduledTimerWithTimeInterval:0.8 target:self
                                                selector:@selector(touchesHold:) userInfo:touches repeats:YES];
    [touchTimer retain];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"タッチイベント終了");
    if ([touchTimer isValid]) [touchTimer invalidate];
}

- (void)touchesHold:(NSTimer *)timer {
    NSLog(@"タッチホールド");
    
    /**
     * ここにホールド時の処理を書く
     * 下記は一例です。(ホールド中に画像が拡大して飛び出ててきます)
     */ 
    // userInfoを取得
    NSSet *touches = [timer userInfo];
    // 座標の取得
    UITouch *aTouch = [touches anyObject];
    CGPoint point = [aTouch locationInView:self.view];
    
    // Imageオブジェクトを生成
    NSString *imgName = [[NSString alloc] initWithFormat:@"popupImageSample.gif"];
    UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
    [self.view addSubview:iv];
    iv.frame = CGRectMake(0, 0, 30, 30);
    iv.center = CGPointMake(point.x, point.y);
    iv.alpha  = 0.8;
    
    // アニメーション処理実装
    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:1.0];  
    [UIView setAnimationDelay:0.0]; 
    [UIView setAnimationRepeatCount:1.0]; 
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn]; 
    [UIView setAnimationDelegate:self]; 
    iv.transform = CGAffineTransformMakeScale(300, 300);
    iv.alpha  = 0.0;
    [UIView commitAnimations];

    // アニメーション終了後にオブジェクトをリリースする
    [self performSelector:@selector(animationStopped:) withObject:iv afterDelay:1.0];
    
    [imgName release];
}

/**
 * アニメーション停止後の処理
 */
- (void)animationStopped:(UIImageView *)ivv
{
    NSLog(@"画像をリリースします");
    [ivv removeFromSuperview];
    [ivv release];
    ivv = NULL;
}

はまったポイント

下記のエラーにはまりました。
touchesHoldメソッドは存在するのに、見つかりませんよと怒られる。

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UIImageView touchesHold:]: unrecognized selector sent to instance 0x4e22670’

解決法がわかると、ほんと大した事なくて凹むのですが・・・。
ただのセレクタの表記ミスでした。
しっかり引数もつけてあげましょう。

(誤) [NSTimer …. selector:@selector(touchesHold:) …..];
(正) [NSTimer …. selector:@selector(touchesHold:withEvent:) …..];