iOS手勢識別的詳細使用(拖動,縮放,旋轉(zhuǎn),點擊,手勢依賴,自定義手勢)

1、UIGestureRecognizer介紹

手勢識別在iOS上非常重要种柑,手勢操作移動設(shè)備的重要特征,極大的增加了移動設(shè)備使用便捷性匹耕。
iOS系統(tǒng)在3.2以后聚请,為方便開發(fā)這使用一些常用的手勢,提供了UIGestureRecognizer類泌神。手勢識別UIGestureRecognizer類是個抽象類良漱,下面的子類是具體的手勢,開發(fā)這可以直接使用這些手勢識別欢际。

UITapGestureRecognizer  
UIPinchGestureRecognizer
UIRotationGestureRecognizer
UISwipeGestureRecognizer
UIPanGestureRecognizer
UILongPressGestureRecognizer

上面的手勢對應(yīng)的操作是:

Tap(點一下)
Pinch(二指往內(nèi)或往外撥動母市,平時經(jīng)常用到的縮放)
Rotation(旋轉(zhuǎn))
Swipe(滑動,快速移動)
Pan (拖移损趋,慢速移動)
 LongPress(長按)

UIGestureRecognizer的繼承關(guān)系如下:


1361893916_7845.png

2患久、使用手勢的步驟

使用手勢很簡單,分為兩步:

  • 創(chuàng)建手勢實例浑槽。當創(chuàng)建手勢時蒋失,指定一個回調(diào)方法,當手勢開始桐玻,改變篙挽、或結(jié)束時,回調(diào)方法被調(diào)用镊靴。
  • 添加到需要識別的View中铣卡。每個手勢只對應(yīng)一個View,當屏幕觸摸在View的邊界內(nèi)時偏竟,如果手勢和預(yù)定的一樣煮落,那就會回調(diào)方法。

ps:一個手勢只能對應(yīng)一個View踊谋,但是一個View可以有多個手勢蝉仇。
建議在真機上運行這些手勢,模擬器操作不太方便,可能導(dǎo)致你認為手勢失效

3殖蚕、Pan 拖動手勢:

UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]];  
snakeImageView.frame = CGRectMake(50, 50, 100, 160);  
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]  
                                                initWithTarget:self  
                                                action:@selector(handlePan:)];      
[snakeImageView addGestureRecognizer:panGestureRecognizer];  
[self.view setBackgroundColor:[UIColor whiteColor]];  
[self.view addSubview:snakeImageView];  

新建一個ImageView轿衔,然后添加手勢
回調(diào)方法:

- (void) handlePan:(UIPanGestureRecognizer*) recognizer  
{  
    CGPoint translation = [recognizer translationInView:self.view];  
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,  
                                   recognizer.view.center.y + translation.y);  
    [recognizer setTranslation:CGPointZero inView:self.view];  
      
}  

4、Pinch縮放手勢

UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];

[snakeImageView addGestureRecognizer:pinchGestureRecognizer];  

回調(diào)方法

- (void) handlePinch:(UIPinchGestureRecognizer*) recognizer  
{  
    recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);  
    recognizer.scale = 1;  
}  

5睦疫、Rotation旋轉(zhuǎn)手勢

UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc]  
                                                 initWithTarget:self  
                                                 action:@selector(handleRotate:)];  
[snakeImageView addGestureRecognizer:rotateRecognizer];  

回調(diào)方法:

- (void) handleRotate:(UIRotationGestureRecognizer*) recognizer  
{  
    recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);  
    recognizer.rotation = 0;  
}  

6呀枢、添加第二個ImagView并添加手勢

記住:一個手勢只能添加到一個View笼痛,兩個View當然要有兩個手勢的實例了

- (void)viewDidLoad  
{  
    [super viewDidLoad];  
  
    UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]];  
    UIImageView *dragonImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dragon.png"]];  
    snakeImageView.frame = CGRectMake(120, 120, 100, 160);  
    dragonImageView.frame = CGRectMake(50, 50, 100, 160);  
    [self.view addSubview:snakeImageView];  
    [self.view addSubview:dragonImageView];  
      
    for (UIView *view in self.view.subviews) {  
        UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]  
                                                        initWithTarget:self  
                                                        action:@selector(handlePan:)];  
          
        UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]  
                                                            initWithTarget:self  
                                                            action:@selector(handlePinch:)];  
          
        UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc]  
                                                         initWithTarget:self  
                                                         action:@selector(handleRotate:)];  
          
        [view addGestureRecognizer:panGestureRecognizer];  
        [view addGestureRecognizer:pinchGestureRecognizer];  
        [view addGestureRecognizer:rotateRecognizer];  
        [view setUserInteractionEnabled:YES];  
    }  
    [self.view setBackgroundColor:[UIColor whiteColor]];       
}  

多添加了一條龍的view裙秋,兩個view都能接收上面的三種手勢琅拌。運行效果如下:


1361954419_1036.png

7、拖動(pan手勢)速度(以較快的速度拖放后view有滑行的效果)

如何實現(xiàn)呢摘刑?
(1)監(jiān)視手勢是否結(jié)束
(2)監(jiān)視觸摸的速度

- (void) handlePan:(UIPanGestureRecognizer*) recognizer  
{  
    CGPoint translation = [recognizer translationInView:self.view];  
    recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,  
                                       recognizer.view.center.y + translation.y);  
    [recognizer setTranslation:CGPointZero inView:self.view];  
      
    if (recognizer.state == UIGestureRecognizerStateEnded) {  
          
        CGPoint velocity = [recognizer velocityInView:self.view];  
        CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));  
        CGFloat slideMult = magnitude / 200;  
        NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);  
          
        float slideFactor = 0.1 * slideMult; // Increase for more of a slide  
        CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor),  
                                         recognizer.view.center.y + (velocity.y * slideFactor));  
        finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);  
        finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);  
          
        [UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{  
            recognizer.view.center = finalPoint;  
        } completion:nil];  
          
    }  

代碼實現(xiàn)解析:

計算速度向量的長度(估計大部分都忘了)這些知識了进宝。
如果速度向量小于200,那就會得到一個小于的小數(shù)枷恕,那么滑行會很短
基于速度和速度因素計算一個終點
確保終點不會跑出父View的邊界
使用UIView動畫使view滑動到終點
運行后党晋,快速拖動圖像view放開會看到view還會在原來的方向滑行一段路。

8徐块、同時觸發(fā)兩個view的手勢

手勢之間是互斥的未玻,如果你想同時觸發(fā)蛇和龍的view,那么需要實現(xiàn)協(xié)議
UIGestureRecognizerDelegate胡控,

@interface ViewController : UIViewController<UIGestureRecognizerDelegate>  
@end  

并在協(xié)議這個方法里返回YES扳剿。

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer  
{  
    return YES;  
}  

把self作為代理設(shè)置給手勢:

panGestureRecognizer.delegate = self;  
pinchGestureRecognizer.delegate = self;  
rotateRecognizer.delegate = self;  

這樣可以同時拖動或旋轉(zhuǎn)縮放兩個view了。

9昼激、tap點擊手勢

這里為了方便看到tap的效果庇绽,當點擊一下屏幕時,播放一個聲音橙困。
為了播放聲音瞧掺,我們加入AVFoundation.framework這個框架。

- (AVAudioPlayer *)loadWav:(NSString *)filename {  
    NSURL * url = [[NSBundle mainBundle] URLForResource:filename withExtension:@"wav"];  
    NSError * error;  
    AVAudioPlayer * player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];  
    if (!player) {  
        NSLog(@"Error loading %@: %@", url, error.localizedDescription);  
    } else {  
        [player prepareToPlay];  
    }  
    return player;  
}  

添加手勢的步驟和前面一樣的凡傅。

#import <UIKit/UIKit.h>  
#import <AVFoundation/AVFoundation.h>  
  
@interface ViewController : UIViewController<UIGestureRecognizerDelegate>  
@property (strong) AVAudioPlayer * chompPlayer;  
@property (strong) AVAudioPlayer * hehePlayer;  
  
@end  

- (void)handleTap:(UITapGestureRecognizer *)recognizer {  
    [self.chompPlayer play];  
}  

運行辟狈,點一下某個圖,就會播放一個咬東西的聲音夏跷。
不過這個點擊播放聲音有點缺陷哼转,就是在慢慢拖動的時候也會播放。這使得兩個手勢重合了拓春。怎么解決呢释簿?使用手勢的:requireGestureRecognizerToFail方法亚隅。

10硼莽、手勢的依賴性

在viewDidLoad的循環(huán)里添加這段代碼:

[tapRecognizer requireGestureRecognizerToFail:panGestureRecognizer];  

意思就是,當如果pan手勢失敗煮纵,就是沒發(fā)生拖動懂鸵,才會出發(fā)tap手勢。這樣如果你有輕微的拖動行疏,那就是pan手勢發(fā)生了匆光。tap的聲音就不會發(fā)出來了。

11.自定義手勢

自定義手勢繼承:UIGestureRecognizer酿联,實現(xiàn)下面的方法:

– touchesBegan:withEvent:  
– touchesMoved:withEvent:  
– touchesEnded:withEvent:  
- touchesCancelled:withEvent:  

新建一個類终息,繼承UIGestureRecognizer夺巩,代碼如下:
.h文件

#import <UIKit/UIKit.h>  
typedef enum {  
    DirectionUnknown = 0,  
    DirectionLeft,  
    DirectionRight  
} Direction;  
  
@interface HappyGestureRecognizer : UIGestureRecognizer  
@property (assign) int tickleCount;  
@property (assign) CGPoint curTickleStart;  
@property (assign) Direction lastDirection;  
  
@end  

.m文件

#import "HappyGestureRecognizer.h"  
#import <UIKit/UIGestureRecognizerSubclass.h>  
#define REQUIRED_TICKLES        2  
#define MOVE_AMT_PER_TICKLE     25  
  
@implementation HappyGestureRecognizer  
  
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
    UITouch * touch = [touches anyObject];  
    self.curTickleStart = [touch locationInView:self.view];  
}  
  
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {  
      
    // Make sure we've moved a minimum amount since curTickleStart  
    UITouch * touch = [touches anyObject];  
    CGPoint ticklePoint = [touch locationInView:self.view];  
    CGFloat moveAmt = ticklePoint.x - self.curTickleStart.x;  
    Direction curDirection;  
    if (moveAmt < 0) {  
        curDirection = DirectionLeft;  
    } else {  
        curDirection = DirectionRight;  
    }  
    if (ABS(moveAmt) < MOVE_AMT_PER_TICKLE) return;  
      
    // 確認方向改變了  
    if (self.lastDirection == DirectionUnknown ||  
        (self.lastDirection == DirectionLeft && curDirection == DirectionRight) ||  
        (self.lastDirection == DirectionRight && curDirection == DirectionLeft)) {  
          
        // 撓癢次數(shù)  
        self.tickleCount++;  
        self.curTickleStart = ticklePoint;  
        self.lastDirection = curDirection;  
          
        // 一旦撓癢次數(shù)超過指定數(shù),設(shè)置手勢為結(jié)束狀態(tài)  
        // 這樣回調(diào)函數(shù)會被調(diào)用周崭。  
        if (self.state == UIGestureRecognizerStatePossible && self.tickleCount > REQUIRED_TICKLES) {  
            [self setState:UIGestureRecognizerStateEnded];  
        }  
    }  
      
}  
  
- (void)reset {  
    self.tickleCount = 0;  
    self.curTickleStart = CGPointZero;  
    self.lastDirection = DirectionUnknown;  
    if (self.state == UIGestureRecognizerStatePossible) {  
        [self setState:UIGestureRecognizerStateFailed];  
    }  
}  
  
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event  
{  
    [self reset];  
}  
  
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event  
{  
    [self reset];  
}  
  
@end  

調(diào)用自定義手勢和上面一樣柳譬,回到這樣寫:

- (void)handleHappy:(HappyGestureRecognizer *)recognizer{  
    [self.hehePlayer play];  
}  

手勢成功后播放呵呵笑的聲音。
在真機上運行续镇,按住某個view美澳,快速左右拖動,就會發(fā)出笑的聲音了摸航。
代碼解析:

先獲取起始坐標:curTickleStart
通過和ticklePoint的x值對比制跟,得出當前的放下是向左還是向右。再算出移動的x的值是否比MOVE_AMT_PER_TICKLE距離大酱虎,如果太則返回雨膨。

再判斷是否有三次是不同方向的動作,如果是則手勢結(jié)束逢净,回調(diào)哥放。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市爹土,隨后出現(xiàn)的幾起案子甥雕,更是在濱河造成了極大的恐慌,老刑警劉巖胀茵,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件社露,死亡現(xiàn)場離奇詭異,居然都是意外死亡琼娘,警方通過查閱死者的電腦和手機峭弟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脱拼,“玉大人瞒瘸,你說我怎么就攤上這事∠ㄅǎ” “怎么了情臭?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赌蔑。 經(jīng)常有香客問我俯在,道長,這世上最難降的妖魔是什么娃惯? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任跷乐,我火速辦了婚禮,結(jié)果婚禮上趾浅,老公的妹妹穿的比我還像新娘愕提。我一直安慰自己馒稍,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布浅侨。 她就那樣靜靜地躺著筷黔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仗颈。 梳的紋絲不亂的頭發(fā)上佛舱,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音挨决,去河邊找鬼请祖。 笑死,一個胖子當著我的面吹牛脖祈,可吹牛的內(nèi)容都是我干的肆捕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼盖高,長吁一口氣:“原來是場噩夢啊……” “哼慎陵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起喻奥,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤席纽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡堤框,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了特笋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舶赔,到底是詐尸還是另有隱情,我是刑警寧澤谦秧,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布竟纳,位于F島的核電站,受9級特大地震影響油够,放射性物質(zhì)發(fā)生泄漏蚁袭。R本人自食惡果不足惜征懈,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一石咬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卖哎,春花似錦鬼悠、人聲如沸删性。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹬挺。三九已至,卻和暖如春它掂,著一層夾襖步出監(jiān)牢的瞬間巴帮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工虐秋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榕茧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓客给,卻偏偏與公主長得像用押,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子靶剑,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容