iOS手勢識別的詳細(xì)使用(拖動,縮放,旋轉(zhuǎn),點擊,手勢依賴,自定義手勢)轉(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)系如下:

2、使用手勢的步驟

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

創(chuàng)建手勢實例。當(dāng)創(chuàng)建手勢時,指定一個回調(diào)方法,當(dāng)手勢開始,改變、或結(jié)束時,回調(diào)方法被調(diào)用。

添加到需要識別的View中。每個手勢只對應(yīng)一個View,當(dāng)屏幕觸摸在View的邊界內(nèi)時,如果手勢和預(yù)定的一樣,那就會回調(diào)方法。

ps:一個手勢只能對應(yīng)一個View,但是一個View可以有多個手勢。

建議在真機上運行這些手勢,模擬器操作不太方便,可能導(dǎo)致你認(rèn)為手勢失效。

3、Pan 拖動手勢:

[cpp]view plaincopy

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)方法:

[cpp]view plaincopy

-?(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縮放手勢

[cpp]view plaincopy

UIPinchGestureRecognizer?*pinchGestureRecognizer?=?[[UIPinchGestureRecognizer?alloc]

initWithTarget:self

action:@selector(handlePinch:)];[snakeImageView?addGestureRecognizer:pinchGestureRecognizer];

[cpp]view plaincopy

-?(void)?handlePinch:(UIPinchGestureRecognizer*)?recognizer

{

recognizer.view.transform?=?CGAffineTransformScale(recognizer.view.transform,?recognizer.scale,?recognizer.scale);

recognizer.scale?=?1;

}

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

[cpp]view plaincopy

UIRotationGestureRecognizer?*rotateRecognizer?=?[[UIRotationGestureRecognizer?alloc]

initWithTarget:self

action:@selector(handleRotate:)];

[snakeImageView?addGestureRecognizer:rotateRecognizer];

[cpp]view plaincopy

-?(void)?handleRotate:(UIRotationGestureRecognizer*)?recognizer

{

recognizer.view.transform?=?CGAffineTransformRotate(recognizer.view.transform,?recognizer.rotation);

recognizer.rotation?=?0;

}

添加了這幾個手勢后,運行看效果,程序中的imageView放了一個

/^\/^\

_|__| ?O|

\/ ? ? /~ ? ? \_/ \

\____|__________/ ?\

\_______ ? ? ?\

`\ ? ? \ ? ? ? ? ? ? ? ? \

| ? ? | ? ? ? ? ? ? ? ? ?\

/ ? ? ?/ ? ? ? ? ? ? ? ? ? ?\

/ ? ? / ? ? ? ? ? ? ? ? ? ? ? \\

/ ? ? ?/ ? ? ? ? ? ? ? ? ? ? ? ? \ \

/ ? ? / ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ ?\

/ ? ? / ? ? ? ? ? ? _----_ ? ? ? ? ? ?\ ? \

/ ? ? / ? ? ? ? ? _-~ ? ? ?~-_ ? ? ? ? | ? |

( ? ? ?( ? ? ? ?_-~ ? ?_--_ ? ?~-_ ? ? _/ ? |

\ ? ? ?~-____-~ ? ?_-~ ? ?~-_ ? ?~-_-~ ? ?/

~-_ ? ? ? ? ? _-~ ? ? ? ? ?~-_ ? ? ? _-~

~--______-~ ? ? ? ? ? ? ? ?~-___-~

的圖片,在模擬器上拖動是沒問題的??s放和旋轉(zhuǎn)有點問題,估計是因為在模擬器上的模擬的兩個接觸點距離在imageView的邊界外了,所以操作無效果。

建議在真機上運行這個手勢。

在模擬器上縮放和選擇的操作技巧:

可以把imageView的frame值設(shè)置大一點,按住alt鍵,按下觸摸板(不按下不行),這樣就可以旋轉(zhuǎn)和縮放了。

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

記?。阂粋€手勢只能添加到一個View,兩個View當(dāng)然要有兩個手勢的實例了

[cpp]view plaincopy

-?(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都能接收上面的三種手勢。運行效果如下:

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

如何實現(xiàn)呢?

監(jiān)視手勢是否結(jié)束

監(jiān)視觸摸的速度

[cpp]view plaincopy

-?(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);

floatslideFactor?=?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,

[cpp]view plaincopy

@interface?ViewController?:?UIViewController

@end

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

[cpp]view plaincopy

-(BOOL)gestureRecognizer:(UIGestureRecognizer?*)gestureRecognizer?shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer?*)otherGestureRecognizer

{

returnYES;

}

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

[cpp]view plaincopy

panGestureRecognizer.delegate?=?self;

pinchGestureRecognizer.delegate?=?self;

rotateRecognizer.delegate?=?self;

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

9、tap點擊手勢

這里為了方便看到tap的效果,當(dāng)點擊一下屏幕時,播放一個聲音。

為了播放聲音,我們加入AVFoundation.framework這個框架。

[cpp]view plaincopy

-?(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];

}

returnplayer;

}

我會在最后例子代碼給出完整代碼,添加手勢的步驟和前面一樣的。

[cpp]view plaincopy

#import?

#import?

@interface?ViewController?:?UIViewController

@property?(strong)?AVAudioPlayer?*?chompPlayer;

@property?(strong)?AVAudioPlayer?*?hehePlayer;

@end

[cpp]view plaincopy

-?(void)handleTap:(UITapGestureRecognizer?*)recognizer?{

[self.chompPlayer?play];

}

運行,點一下某個圖,就會播放一個咬東西的聲音。

不過這個點擊播放聲音有點缺陷,就是在慢慢拖動的時候也會播放。這使得兩個手勢重合了。怎么解決呢?使用手勢的:requireGestureRecognizerToFail方法。

10、手勢的依賴性

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

[cpp]view plaincopy

[tapRecognizer?requireGestureRecognizerToFail:panGestureRecognizer];

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

11、自定義手勢

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

[cpp]view plaincopy

–?touchesBegan:withEvent:

–?touchesMoved:withEvent:

–?touchesEnded:withEvent:

-?touchesCancelled:withEvent:

新建一個類,繼承UIGestureRecognizer,代碼如下:

.h文件

[cpp]view plaincopy

#import?

typedefenum{

DirectionUnknown?=?0,

DirectionLeft,

DirectionRight

}?Direction;

@interface?HappyGestureRecognizer?:?UIGestureRecognizer

@property?(assign)inttickleCount;

@property?(assign)?CGPoint?curTickleStart;

@property?(assign)?Direction?lastDirection;

@end

.m文件

[cpp]view plaincopy

#import?"HappyGestureRecognizer.h"

#import?

#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;

//?確認(rèn)方向改變了

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)用自定義手勢和上面一樣,回到這樣寫:

[cpp]view plaincopy

-?(void)handleHappy:(HappyGestureRecognizer?*)recognizer{

[self.hehePlayer?play];

}

手勢成功后播放呵呵笑的聲音。

在真機上運行,按住某個view,快速左右拖動,就會發(fā)出笑的聲音了。

代碼解析:

先獲取起始坐標(biāo):curTickleStart

通過和ticklePoint的x值對比,得出當(dāng)前的放下是向左還是向右。再算出移動的x的值是否比MOVE_AMT_PER_TICKLE距離大,如果太則返回。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容