顯示具有 Tap 標籤的文章。 顯示所有文章
顯示具有 Tap 標籤的文章。 顯示所有文章

2014年2月21日 星期五

偵測多指 Tap

我們可以藉由 UITapGestureRecognizer 來實作多重點按這個手勢。
它可以設定成,發生特定點按次數時,執行指定的方法。

但這邊有個問題,假設我們想在一個 View 上實作 user 點按 1 次跟連點 2 次時會發生不同動作,
程式碼大概是這樣:
    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap1)];
    singleTap.numberOfTapsRequired = 1;
    [self.view addGestureRecognizer:singleTap];
    
    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap2)];
    doubleTap.numberOfTapsRequired = 2;
    [self.view addGestureRecognizer:doubleTap];
問題在於兩個辨識器並不知道彼此的存在,所以當 user 執行連按 2 次這個動作時,
不只 tap2 這個 method 會被呼叫,連 tap1 也會被呼叫,而且是兩次,

解決方法是建立個失敗的(failure)的需求,告訴 tap1 只有在 tap2 無法辨識及回應 user 的輸入時,
才會去觸發其動作。

    [singleTap requireGestureRecognizerToFail:doubleTap];

實作:
#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UILabel *singleLabel;
@property (weak, nonatomic) IBOutlet UILabel *doubleLabel;
@property (weak, nonatomic) IBOutlet UILabel *tripleLabel;
@property (weak, nonatomic) IBOutlet UILabel *quadrupleLabel;

- (void)tap1;
- (void)tap2;
- (void)tap3;
- (void)tap4;

- (void)eraseMe:(UILabel *)label;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib
    
    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap1)];
    singleTap.numberOfTapsRequired = 1;
    singleTap.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:singleTap];
    
    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap2)];
    doubleTap.numberOfTapsRequired = 2;
    doubleTap.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:doubleTap];
    [singleTap requireGestureRecognizerToFail:doubleTap];
    
    UITapGestureRecognizer *tripleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap3)];
    tripleTap.numberOfTapsRequired = 3;
    tripleTap.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:tripleTap];
    [doubleTap requireGestureRecognizerToFail:tripleTap];
    
    UITapGestureRecognizer *quadrupleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap4)];
    quadrupleTap.numberOfTapsRequired = 4;
    quadrupleTap.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:quadrupleTap];
    [tripleTap requireGestureRecognizerToFail:quadrupleTap];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    _singleLabel = nil;
    _doubleLabel = nil;
    _tripleLabel = nil;
    _quadrupleLabel = nil;
}

#pragma mark - Added methods

- (void)tap1 {

    _singleLabel.text = @"Single Tap Detected";
    [self performSelector:@selector(eraseMe:) withObject:_singleLabel afterDelay:1.6f];
}

- (void)tap2 {
    _doubleLabel.text = @"Double Tap Detected";
    [self performSelector:@selector(eraseMe:) withObject:_doubleLabel afterDelay:1.6f];
}

- (void)tap3 {
    _tripleLabel.text = @"Triple Tap Detected";
    [self performSelector:@selector(eraseMe:) withObject:_tripleLabel afterDelay:1.6f];
}

- (void)tap4 {
    _quadrupleLabel.text = @"Quadruple Tap Detected";
    [self performSelector:@selector(eraseMe:) withObject:_quadrupleLabel afterDelay:1.6f];
}

- (void)eraseMe:(UILabel *)label {
    label.text = @"";
}

@end
以上我們可看出,當 user 點按 1 下螢幕時,
singleTap 需要等到 doubleTap 的失敗才能觸發自己的事件,
如果 user 在系統設定的點按間隔時間內按下第二次,
則此手勢便會視為點按 2 次,並跳過 singleTap 而去執行 doubleTap 所指定的動作。
反之,若 user 沒在點按間隔時間內按下第二次,
則 doubleTap 會告訴 singleTap 它失敗了,singleTap 才會開始執行其動作。

以此類推,doubleTap 需要 tripleTap 的失敗、tripleTap 需要 quadrupleTap 的失敗。