1、UIGestureRecognizer 介绍手势识别在 iOS 中非常重要,他极大地提高了移动设备的使用便捷性。
iOS 系统在 3.2 以后,他提供了一些常用的手势(UIGestureRecognizer 的子类),开发者可以直接使用他们进行手势操作。
UipanGestureRecognizer(拖动)
UIPinchGestureRecognizer(捏合)
UIRotationGestureRecognizer(旋转)
UITapGestureRecognizer(点按)
UILongPRessGestureRecognizer(长按)
UISwipeGestureRecognizer(轻扫)
另外,可以通过继承 UIGestureRecognizer 类,实现自定义手势(手势识别器类)。
PS:自定义手势时,需要 #import <UIKit/UIGestureRecognizerSubclass.h>,一般需实现如下方法:
1- (void)reset;23- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;4- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;5- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;6- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;7//以上方法在分类 UIGestureRecognizer (UIGestureRecognizerProtected) 中声明,更多方法声明请自行查看
UIGestureRecognizer 的继承关系如下:
2、手势状态在六种手势识别中,只有一种手势是离散型手势,他就是 UITapGestureRecognizer。
离散型手势的特点就是:一旦识别就无法取消,而且只会调用一次手势操作事件(初始化手势时指定的回调方法)。
换句话说其他五种手势是连续型手势,而连续型手势的特点就是:会多次调用手势操作事件,而且在连续手势识别后可以取消手势。从下图可以看出两者调用操作事件的次数是不同的:
手势状态枚举如下:
1typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {2UIGestureRecognizerStatePossible,//尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态3UIGestureRecognizerStateBegan,//手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成4UIGestureRecognizerStateChanged,//手势状态发生转变5UIGestureRecognizerStateEnded,//手势识别操作完成(此时已经松开手指)6UIGestureRecognizerStateCancelled,//手势被取消,恢复到默认状态7UIGestureRecognizerStateFailed,//手势识别失败,恢复到默认状态8UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded//手势识别完成,同UIGestureRecognizerStateEnded9};
对于离散型手势 UITapGestureRecgnizer 要么被识别,要么失败,点按(假设点按次数设置为1,并且没有添加长按手势)下去一次不松开则此时什么也不会发生,松开手指立即识别并调用操作事件,并且状态为3(已完成)。但是连续型手势要复杂一些,就拿旋转手势来说,如果两个手指点下去不做任何操作,此时并不能识别手势(因为我们还没旋转)但是其实已经触发了触摸开始事件,此时处于状态0;如果此时旋转会被识别,也就会调用对应的操作事件,同时状态变成1(手势开始),但是状态1只有一瞬间;紧接着状态变为2(因为我们的旋转需要持续一会),并且重复调用操作事件(如果在事件中打印状态会重复打印2);松开手指,此时状态变为3,并调用1次操作事件。
3、使用手势的步骤使用手势很简单,分为三步:
创建手势识别器对象实例。创建时,指定一个回调方法,当手势开始,改变、或结束时,执行回调方法。
设置手势识别器对象实例的相关属性(可选部分)
添加到需要识别的 View 中。每个手势只对应一个 View,当屏幕触摸在 View 的边界内时,如果手势和预定的一样,那就会执行回调方法。
PS:一个手势只能对应一个 View,但是一个 View 可以有多个手势。建议在真机上测试这些手势,模拟器操作不太方便,可能导致认为手势失效的情况。(模拟器测试捏合和旋转手势时,按住 option 键,再用触摸板或鼠标操作)
4、举例说明 功能描述:
附加到两个图片视图 UIImageView 的有『拖动』、『捏合』、『旋转』、『点按』;
而『轻扫』和『自定义手势 KMGestureRecognizer』附加在根视图 UIView 中。
拖动:进行当前图片视图位置移动
捏合:进行当前图片视图缩放
旋转:进行当前图片视图角度旋转
点按:双击恢复当前图片视图的缩放、角度旋转、不透明度
长按:设置当前图片视图的不透明度为0.7
轻扫:左右轻扫设置两个图片视图为居中,同时以垂直居中的特定偏移量定位
自定义手势:挠痒功能,左右扫动共3次或以上,设置两个图片视图为居中,同时以水平居中的特定偏移量定位
效果如下:
KMGestureRecognizer.h
1#import<UIKit/UIKit.h>23typedef NS_ENUM(NSUInteger, Direction) {4DirectionUnknown,5DirectionLeft,6DirectionRight7};89@interfaceKMGestureRecognizer : UIGestureRecognizer10@property (assign, nonatomic) NSUInteger tickleCount;//挠痒次数11@property (assign, nonatomic) CGPoint currentTickleStart;//当前挠痒开始坐标位置12@property (assign, nonatomic) Direction lastDirection;//最后一次挠痒方向1314@end
KMGestureRecognizer.m
1#import"KMGestureRecognizer.h"2#import<UIKit/UIGestureRecognizerSubclass.h>34@implementationKMGestureRecognizer5#definekMinTickleSpacing 20.06#definekMaxTickleCount 378- (void)reset {9_tickleCount =0;10_currentTickleStart =CGPointZero;11_lastDirection =DirectionUnknown;1213if(self.state ==UIGestureRecognizerStatePossible) {14self.state =UIGestureRecognizerStateFailed;15}16}1718- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{19UITouch *touch =[touches anyObject];20_currentTickleStart = [touch locationInView:self.view];//设置当前挠痒开始坐标位置21}2223- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{24//『当前挠痒开始坐标位置』和『移动后坐标位置』进行 X 轴值比较,得到是向左还是向右移动25UITouch *touch =[touches anyObject];26CGPoint tickleEnd =[touch locationInView:self.view];27CGFloat tickleSpacing = tickleEnd.x -_currentTickleStart.x;28Direction currentDirection = tickleSpacing <0?DirectionLeft : DirectionRight;2930//移动的 X 轴间距值是否符合要求,足够大31if(ABS(tickleSpacing) >=kMinTickleSpacing) {32//判断是否有三次不同方向的动作,如果有则手势结束,将执行回调方法33if(_lastDirection == DirectionUnknown ||34(_lastDirection == DirectionLeft && currentDirection == DirectionRight) ||35(_lastDirection == DirectionRight && currentDirection ==DirectionLeft)) {36_tickleCount++;37_currentTickleStart =tickleEnd;38_lastDirection =currentDirection;3940if(_tickleCount >= kMaxTickleCount && self.state ==UIGestureRecognizerStatePossible) {41self.state =UIGestureRecognizerStateEnded;42//NSLog(@"自定义手势成功,将执行回调方法");43}44}45}46}4748- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{49[self reset];50}5152- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{53[self reset];54}5556@end
ViewController.h
1#import<UIKit/UIKit.h>2#import"KMGestureRecognizer.h"34@interfaceViewController : UIViewController5@property (strong, nonatomic) UIImageView *imgV;6@property (strong, nonatomic) UIImageView *imgV2;7@property (strong, nonatomic) KMGestureRecognizer *customGestureRecognizer;89@end
ViewController.m
1#import"ViewController.h"23@interfaceViewController ()4- (void)handlePan:(UIPanGestureRecognizer *)recognizer;5- (void)handlePinch:(UIPinchGestureRecognizer *)recognizer;6- (void)handleRotation:(UIRotationGestureRecognizer *)recognizer;7- (void)handleTap:(UITapGestureRecognizer *)recognizer;8- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer;9- (void)handleSwipe:(UISwipeGestureRecognizer *)recognizer;10- (void)handleCustomGestureRecognizer:(KMGestureRecognizer *)recognizer;1112- (void)bindPan:(UIImageView *)imgVCustom;13- (void)bindPinch:(UIImageView *)imgVCustom;14- (void)bindRotation:(UIImageView *)imgVCustom;15- (void)bindTap:(UIImageView *)imgVCustom;16- (void)bindLongPress:(UIImageView *)imgVCustom;17- (void)bindSwipe;18- (void)bingCustomGestureRecognizer;19- (void)layoutUI;20@end2122@implementationViewController2324- (void)viewDidLoad {25[super viewDidLoad];2627[self layoutUI];28}2930- (void)didReceiveMemoryWarning {31[super didReceiveMemoryWarning];32//Dispose of any resources that can be recreated.33}3435#pragmamark - 处理手势操作36/**37* 处理拖动手势38*39* @param recognizer 拖动手势识别器对象实例40*/41- (void)handlePan:(UIPanGestureRecognizer *)recognizer {42//视图前置操作43[recognizer.view.superview bringSubviewToFront:recognizer.view];4445CGPoint center =recognizer.view.center;46CGFloat cornerRadius = recognizer.view.frame.size.width /2;47CGPoint translation =[recognizer translationInView:self.view];48//NSLog(@"%@", NSStringFromCGPoint(translation));49recognizer.view.center = CGPointMake(center.x + translation.x, center.y +translation.y);50[recognizer setTranslation:CGPointZero inView:self.view];5152if(recognizer.state ==UIGestureRecognizerStateEnded) {53//计算速度向量的长度,当他小于200时,滑行会很短54CGPoint velocity =[recognizer velocityInView:self.view];55CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y *velocity.y));56CGFloat slideMult = magnitude /200;57//NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);//e.g. 397.973175, slideMult: 1.9898665859//基于速度和速度因素计算一个终点60floatslideFactor =0.1*slideMult;61CGPoint finalPoint = CGPointMake(center.x + (velocity.x *slideFactor),62center.y + (velocity.y *slideFactor));63//限制最小[cornerRadius]和最大边界值[self.view.bounds.size.width - cornerRadius],以免拖动出屏幕界限64finalPoint.x =MIN(MAX(finalPoint.x, cornerRadius),65self.view.bounds.size.width -cornerRadius);66finalPoint.y =MIN(MAX(finalPoint.y, cornerRadius),67self.view.bounds.size.height -cornerRadius);6869//使用 UIView 动画使 view 滑行到终点70[UIView animateWithDuration:slideFactor*271delay:072options:UIViewAnimationOptionCurveEaSEOut73animations:^{74recognizer.view.center =finalPoint;75}76completion:nil];77}78}7980/**81* 处理捏合手势82*83* @param recognizer 捏合手势识别器对象实例84*/85- (void)handlePinch:(UIPinchGestureRecognizer *)recognizer {86CGFloat scale =recognizer.scale;87recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, scale, scale);//在已缩放大小基础下进行累加变化;区别于:使用 CGAffineTransformMakeScale 方法就是在原大小基础下进行变化88recognizer.scale =1.0;89}9091/**92* 处理旋转手势93*94* @param recognizer 旋转手势识别器对象实例95*/96- (void)handleRotation:(UIRotationGestureRecognizer *)recognizer {97recognizer.view.transform =CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);98recognizer.rotation =0.0;99}100101/**102* 处理点按手势103*104* @param recognizer 点按手势识别器对象实例105*/106- (void)handleTap:(UITapGestureRecognizer *)recognizer {107UIView *view =recognizer.view;108view.transform = CGAffineTransformMakeScale(1.0,1.0);109view.transform = CGAffineTransformMakeRotation(0.0);110view.alpha =1.0;111}112113/**114* 处理长按手势115*116* @param recognizer 点按手势识别器对象实例117*/118- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer {119//长按的时候,设置不透明度为0.7120recognizer.view.alpha =0.7;121}122123/**124* 处理轻扫手势125*126* @param recognizer 轻扫手势识别器对象实例127*/128- (void)handleSwipe:(UISwipeGestureRecognizer *)recognizer {129//代码块方式封装操作方法130void(^positionOperation)() = ^() {131CGPoint newPoint =recognizer.view.center;132newPoint.y -=20.0;133_imgV.center =newPoint;134135newPoint.y +=40.0;136_imgV2.center =newPoint;137};138139//根据轻扫方向,进行不同控制140switch(recognizer.direction) {141caseUISwipeGestureRecognizerDirectionRight: {142positionOperation();143
break;144}145caseUISwipeGestureRecognizerDirectionLeft: {146positionOperation();147
break;148}149caseUISwipeGestureRecognizerDirectionUp: {150
break;151}152caseUISwipeGestureRecognizerDirectionDown: {153
break;154}155}156}157158/**159* 处理自定义手势160*161* @param recognizer 自定义手势识别器对象实例162*/163- (void)handleCustomGestureRecognizer:(KMGestureRecognizer *)recognizer {164//代码块方式封装操作方法165void(^positionOperation)() = ^() {166CGPoint newPoint =recognizer.view.center;167newPoint.x -=20.0;168_imgV.center =newPoint;169170newPoint.x +=40.0;171_imgV2.center =newPoint;172};173174positionOperation();175}176177178#pragmamark - 绑定手势操作179/**180* 绑定拖动手势181*182* @param imgVCustom 绑定到图片视图对象实例183*/184- (void)bindPan:(UIImageView *)imgVCustom {185UIPanGestureRecognizer *recognizer =[[UIPanGestureRecognizer alloc] initWithTarget:self186action:@selector(handlePan:)];187[imgVCustom addGestureRecognizer:recognizer];188}189190/**191* 绑定捏合手势192*193* @param imgVCustom 绑定到图片视图对象实例194*/195- (void)bindPinch:(UIImageView *)imgVCustom {196UIPinchGestureRecognizer *recognizer =[[UIPinchGestureRecognizer alloc] initWithTarget:self197action:@selector(handlePinch:)];198[imgVCustom addGestureRecognizer:recognizer];199//[recognizer requireGestureRecognizerToFail:imgVCustom.gestureRecognizers.firstObject];200}201202/**203* 绑定旋转手势204*205* @param imgVCustom 绑定到图片视图对象实例206*/207- (void)bindRotation:(UIImageView *)imgVCustom {208UIRotationGestureRecognizer *recognizer =[[UIRotationGestureRecognizer alloc] initWithTarget:self209action:@selector(handleRotation:)];210[imgVCustom addGestureRecognizer:recognizer];211}212213/**214* 绑定点按手势215*216* @param imgVCustom 绑定到图片视图对象实例217*/218- (void)bindTap:(UIImageView *)imgVCustom {219UITapGestureRecognizer *recognizer =[[UITapGestureRecognizer alloc] initWithTarget:self220action:@selector(handleTap:)];221//使用一根手指双击时,才触发点按手势识别器222recognizer.numberOfTapsRequired =2;223recognizer.numberOfTouchesRequired =1;224[imgVCustom addGestureRecognizer:recognizer];225}226227/**228* 绑定长按手势229*230* @param imgVCustom 绑定到图片视图对象实例231*/232- (void)bindLongPress:(UIImageView *)imgVCustom {233UILongPressGestureRecognizer *recognizer =[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];234recognizer.minimumPressDuration =0.5;//设置最小长按时间;默认为0.5秒235[imgVCustom addGestureRecognizer:recognizer];236}237238/**239* 绑定轻扫手势;支持四个方向的轻扫,但是不同的方向要分别定义轻扫手势240*/241- (void)bindSwipe {242//向右轻扫手势243UISwipeGestureRecognizer *recognizer =[[UISwipeGestureRecognizer alloc] initWithTarget:self244action:@selector(handleSwipe:)];245recognizer.direction = UISwipeGestureRecognizerDirectionRight;//设置轻扫方向;默认是 UISwipeGestureRecognizerDirectionRight,即向右轻扫246[self.view addGestureRecognizer:recognizer];247[recognizer requireGestureRecognizerToFail:_customGestureRecognizer];//设置以自定义挠痒手势优先识别248249//向左轻扫手势250recognizer =[[UISwipeGestureRecognizer alloc] initWithTarget:self251action:@selector(handleSwipe:)];252recognizer.direction =UISwipeGestureRecognizerDirectionLeft;253[self.view addGestureRecognizer:recognizer];254[recognizer requireGestureRecognizerToFail:_customGestureRecognizer];//设置以自定义挠痒手势优先识别255}256257/**258* 绑定自定义挠痒手势;判断是否有三次不同方向的动作,如果有则手势结束,将执行回调方法259*/260- (void)bingCustomGestureRecognizer {261//当 recognizer.state 为 UIGestureRecognizerStateEnded 时,才执行回调方法 handleCustomGestureRecognizer:262263//_customGestureRecognizer = [KMGestureRecognizer new];264_customGestureRecognizer =[[KMGestureRecognizer alloc] initWithTarget:self265action:@selector(handleCustomGestureRecognizer:)];266[self.view addGestureRecognizer:_customGestureRecognizer];267}268269- (void)layoutUI {270//图片视图 _imgV271UIImage *img = [UIImage imageNamed:@"Emoticon_tusiji_icon"];272CGFloat cornerRadius =img.size.width;273_imgV =[[UIImageView alloc] initWithImage:img];274_imgV.frame = CGRectMake(20.0,20.0,275cornerRadius *2, cornerRadius *2);276_imgV.userInteractionEnabled =YES;277_imgV.layer.masksToBounds =YES;278_imgV.layer.cornerRadius =cornerRadius;279_imgV.layer.borderWidth =2.0;280_imgV.layer.borderColor =[UIColor grayColor].CGColor;281[self.view addSubview:_imgV];282283//图片视图 _imgV2284img = [UIImage imageNamed:@"Emoticon_tusiji_icon2"];285cornerRadius =img.size.width;286_imgV2 =[[UIImageView alloc] initWithImage:img];287_imgV2.frame = CGRectMake(20.0,40.0+_imgV.frame.size.height,288cornerRadius *2, cornerRadius *2);289_imgV2.userInteractionEnabled =YES;290_imgV2.layer.masksToBounds =YES;291_imgV2.layer.cornerRadius =cornerRadius;292_imgV2.layer.borderWidth =2.0;293_imgV2.layer.borderColor =[UIColor orangeColor].CGColor;294[self.view addSubview:_imgV2];295296297[self bindPan:_imgV];298[self bindPinch:_imgV];299[self bindRotation:_imgV];300[self bindTap:_imgV];301[self bindLongPress:_imgV];302303[self bindPan:_imgV2];304[self bindPinch:_imgV2];305[self bindRotation:_imgV2];306[self bindTap:_imgV2];307[self bindLongPress:_imgV2];308309//为了处理手势识别优先级的问题,这里需先绑定自定义挠痒手势310[self bingCustomGestureRecognizer];311[self bindSwipe];312}313314@end