iOS 幫我們內建了好用的手勢辨識器:
UIGestureRecognizer 類別。
有了它就不需要觀察所有的事件來查看手指如何移動。
接下來我們要實作--偵測 Swipe(滑動)
先來看看沒有使用手勢辨識器的程式碼:
#import "ViewController.h"
#define kMinimumGestureLength 25
#define kMaximumVariance 5
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property CGPoint gestureStartPoint;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
_label = nil;
}
#pragma mark - Added methods
- (void)eraseText {
_label.text = @"";
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
_gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(_gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(_gestureStartPoint.y - currentPosition.y);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
_label.text = @"Horizontal swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
} else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
_label.text = @"Vertical swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
}
@end
#define kMinimumGestureLength 25
#define kMaximumVariance 5
首先,我們先定義一個以像素計算的最小手勢長度,user 必須滑超過這個距離,手勢才會被當成 Swipe 計算。
然後再定義一個變異量,它是 user 偏離直線的最大距離,只要手勢偏離的程度不超過它就會被當成水平或垂直滑動來計算。
以上我們所定義的最小手勢長度是 25 像素、變異值是 5 像素。
只要 user 水平移動超過 25 像素,則手勢和起點的垂直位置上下最多可差到 5 像素。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
_gestureStartPoint = [touch locationInView:self.view];
}
以上method 主要是在抓取任何一個觸控,並儲存它的觸控點。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(_gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(_gestureStartPoint.y - currentPosition.y);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
_label.text = @"Horizontal swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
} else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
_label.text = @"Vertical swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
}
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
取得 user 手指目前的位置。
CGFloat deltaX = fabsf(_gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(_gestureStartPoint.y - currentPosition.y);
計算 user 手指從其起點位置向水平和垂直方向個移動多少距離。
fabsf()函式是來自C算術程式庫,它會回傳一個 float 的絕對值。在此的用意是讓我們兩個值相減而不用擔心出現負值。
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
_label.text = @"Horizontal swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
} else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
_label.text = @"Vertical swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
檢查 user 是否在同一個方向移動超過最小值,且變異值小於另一個移動方向,已形成一個手勢。
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
讓文字出現兩秒後將它清除。
手勢辨識器:
我們不會直接使用 UIGestureRecognizer,而是建立它的 subclass 的實體,這些實體每個都是為了特定的手勢所設計,
像是 Swipe、Tap、Pinch。
讓我們以手勢辨識器改寫上例程式:
#import "ViewController.h"
//#define kMinimumGestureLength 25
//#define kMaximumVariance 5
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
//@property CGPoint gestureStartPoint;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *vertical = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(reportVerticalSwipe:)];
vertical.direction = UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:vertical];
UISwipeGestureRecognizer *horizontal = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(reportHorizontalSwipe:)];
horizontal.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:horizontal];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
_label = nil;
}
#pragma mark - Added methods
- (void)eraseText {
_label.text = @"";
}
/*
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
_gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(_gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(_gestureStartPoint.y - currentPosition.y);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
_label.text = @"Horizontal swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
} else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
_label.text = @"Vertical swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
} */
- (void)reportHorizontalSwipe:(UIGestureRecognizer *)recognizer {
_label.text = @"Horizontal swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
- (void)reportVerticalSwipe:(UIGestureRecognizer *)recognizer {
_label.text = @"Vertical swipe detected";
[self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
@end
首先,我們將 touchesBegan:withEvent:、touchesMoved:withEvent: 刪除,
新增了 reportHorizontalSwipe:、reportVerticalSwipe:。
這兩個 method 實作了滑動手勢的功能,就像之前刪除的 touchesBegan:withEvent:、touchesMoved:withEvent: 一樣。
接著,在 viewDidLoad 中新增程式碼:
UISwipeGestureRecognizer *vertical = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(reportVerticalSwipe:)];
vertical.direction = UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:vertical];
UISwipeGestureRecognizer *horizontal = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(reportHorizontalSwipe:)];
horizontal.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:horizontal];
以上我們可以看到,藉由 UISwipeGestureRecognizer 我們只需要建立並且設定一些手勢辨識器,
並將它增加到 View 中,即可完成跟上一個範例同樣的功能。
當 user 使用其中一種手勢辨識器認得的方法和螢幕互動時,我們指定的方法就會被呼叫。