第一章:多点触摸与手势检测
2016-06-20 16:13
120 查看
一、响应者链
只要继承了UIResponder的对象就可作为事件的响应者,实际上UIControl继承了UIView,UIView又继承了UIResponder,由此可见,所有的对象都可作为事件的响应者。当用户与某个控件交互时,该控件将作为“第一响应者(First Responder)”,第一响应者将作为响应者链的开始,该事件首先被发送给第一响应者(也就是用户触摸屏幕的控件)。事件将沿着响应者链一直向下传播,直到被某个响应者处理。事件响应者链的典型传播路线如下:
First Responder —> First Responder的视图控制器(如果有)—>父容器(如果有)—>父容器的视图控制器(如果有)—>UIWindow—>UIApplication—>应用程序委托对象
如果某个响应者“截获”了某个事件,那么该响应者要根据条件决定是否处理该事件。当响应者无法处理该事件时,则需要在处理方法中手动传递该事件。例如如下代码:
- (void)handleTapEvent:(UITapGestureRecognizer *)event{ if (YES) { //处理事件 }else{ OtherTapView* view = (OtherTapView *)self.nextResponder; [view handleTapEvent:event]; } }
二、响应触碰方法
如果希望自定义控件可以响应用户的触碰事件,则可以通过通过UIResponder的如下四个方法实现:①、- (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event;//当用户手指开始接触控件或窗口事件时激发该方法;
②、- (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event;//当用户手指在控件上移动时激发该方法;
③、- (void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event;//当用户手指结束触碰控件激发该方法;
④、- (void)touchesCancelled:(NSSet )touches withEvent:(UIEvent )event;//当系统事件(比如内存底事件)终止了触碰事件时激发该方法。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"%ld",[touches count]); NSLog(@"%ld",[[touches anyObject]tapCount]); CGPoint point = [[touches anyObject] locationInView:self.view]; }
注意:
①、第一个NSSet类型的参数代表了用户同时触碰控件的多个手指,如果该用户用3个手指触碰该控件,那么该touches参数中将会包含3个UITouch对象;
②、UITouch对象代表一个触碰事件,该对象提供了一个tapCount属性,该属性用于返回用户触碰屏幕的次数,比如用户只是轻轻的触碰屏幕一次,那么该属性将返回1.如果该属性返回3,则代表用户快速触碰了3次屏幕.
③、UITouch还提供了locationInView:方法来获取该触碰事件在UIView控件中的碰触位置.
三、使用手势处理器(UIGestureRecognizer)
UIGestureRecognizer提供了如下子类:①、UITapGestureRecognizer:处理用户点击手势的手势处理器;
②、UIPinchGestureRecognizer:处理用户捏合手势的手势处理器;
③、UIRotationGestureRecognizer:处理用户旋转手势的手势处理器;
④、UISwipeGestureRecognizer:处理用户滑动手势的手势处理器;
⑤、UIPanGestureRecognizer:处理用户拖动手势的手势处理器;
⑥、UILongPressGestureRecognizer:处理用户长按手势的手势处理器;
使用手势处理器处理用户触碰手势的编程步骤如下:
、根据程序要处理的手势创建对象的手势处理器对象。创建手势处理器时需要制定target和action参数—当该控件上发生触碰手势后,该target对象的action方法将会被激发。
、如果该UI控件不允许交互,则将该UI控件的userInteractionEnable属性设为YES;如果希望该控件可支持多点触碰,还需要将multipleTouchEnable设为YES.
、调用UI控件的addGestureRecognizer:方法添加该手势处理器;
UIGestureRecognizer作为所有手势处理器的基类,它提供了如下常用的方法和属性:
(1)、- (CGPoint)locationInView:(UIView*)view;返回该手势在view控件中的触碰位置;
(2)、- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView*)view;返回该手势中第touchIndex个触碰点在view控件中的位置;
(3)、- (NSUInteger)numberOfTouches;返回该手势包含触碰点的数量(也就是用户用了几个手指进行触碰);
(4)、@property(nonatomic,readonly) UIView *view;返回激发该手势的UI控件;
(5)、@property(nonatomic, getter=isEnabled) BOOL enabled;用户设置和返回该手势处理器是否可用;
(6)、@property(nonatomic,readonly) UIGestureRecognizerState state;获取该手势所处的状态,该状态分为如下几种:
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) { UIGestureRecognizerStatePossible, UIGestureRecognizerStateBegan, UIGestureRecognizerStateChanged, UIGestureRecognizerStateEnded, UIGestureRecognizerStateCancelled, UIGestureRecognizerStateFailed, UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded };
1、使用UITapGestureRecognizer处理点击事件
UITapGestureRecognizer还提供了如下两个属性:(1)、@property (nonatomic) NSUInteger numberOfTapsRequired;指定该手势处理器只处理几次触碰事件;
(2)、@property (nonatomic) NSUInteger numberOfTouchesRequired;指定该手势处理器只处理几个手指的触碰事件.
实例:
- (void)viewDidLoad { [super viewDidLoad]; gv = [[UIView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; label = [[UILabel alloc]initWithFrame:CGRectMake(20, 50, gv.frame.size.width - 40, 40)]; [gv addSubview:label]; self.view.backgroundColor = [UIColor lightGrayColor]; for (int i = 0; i < 6; i ++) { //创建手势处理器,指定使用该控制器的handleTap:方法处理手势 UITapGestureRecognizer* gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTap:)]; //设置该点击手势处理器只处理i次连击事件 gesture.numberOfTapsRequired = i; //设置该点击手势处理器只处理两个手指的触碰事件 gesture.numberOfTouchesRequired = 2; //为gv控件添加手势处理器 [gv addGestureRecognizer:gesture]; } } - (void)handleTap:(UITapGestureRecognizer *)gesture{ NSInteger touchNum = gesture.numberOfTouches; NSUInteger tapNum = gesture.numberOfTapsRequired; label.text = [NSString stringWithFormat:@"用户使用%ld个手指进行触碰,触碰次数为:%ld",touchNum,tapNum]; //指定2秒后清除label的文本 [label performSelector:@selector(setText:) withObject:@"" afterDelay:5]; }
2、使用UIPinchGestureRecognizer处理捏合事件
UIPinchGestureRecognizer定义了如下两个属性来获取捏合相关信息:(1)、@property (nonatomic) CGFloat scale;获取捏合的比例;
(2)、@property (nonatomic,readonly) CGFloat velocity;获取捏合的速度;
实例:
- (void)viewDidLoad { [super viewDidLoad]; gv = [[UIView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; label = [[UILabel alloc]initWithFrame:CGRectMake(20, 50, gv.frame.size.width - 40, 80)]; label.numberOfLines = 2; [gv addSubview:label]; self.view.backgroundColor = [UIColor lightGrayColor]; UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)]; //为gv控件添加手势处理器 [gv addGestureRecognizer:gesture]; } - (void)handlePinch:(UIPinchGestureRecognizer *)gesture{ //获取用户捏合的速度和比例 CGFloat velocity = gesture.velocity; CGFloat scale = gesture.scale; label.text = [NSString stringWithFormat:@"用户捏合的速度为:%g,比例为:%g",velocity,scale]; //指定2秒后清除label的文本 [label performSelector:@selector(setText:) withObject:@"" afterDelay:5]; }
实例:通过捏合手势缩放图片
- (void)viewDidLoad { [super viewDidLoad]; [UIApplication sharedApplication].statusBarHidden = YES; srcImage = [UIImage imageNamed:@"妞.png"]; //设置图片直接显示在中间(不进行任何缩放) self.view.contentMode = UIViewContentModeCenter; self.view.backgroundColor = [UIColor lightGrayColor]; gv = [[UIImageView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; currentSize = CGSizeMake(self.view.frame.size.width - 20, 200); gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; gv.image = srcImage; //创建UIPinchGestureRecognizer手势处理器,该手势处理器激发scaleImage:方法 UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(scaleImage:)]; [gv addGestureRecognizer:gesture]; } //imageView进行缩放 - (void)scaleImage:(UIPinchGestureRecognizer *)gesture{ gv.bounds = CGRectMake(0, 0, currentSize.width * gesture.scale, currentSize.height * gesture.scale); if (gesture.state == UIGestureRecognizerStateEnded) { currentSize = CGSizeMake(currentSize.width * gesture.scale, currentSize.height * gesture.scale); } }
3、使用UIRotationGestureRecognizer处理旋转手势
UIRotationGestureRecognizer定义了如下两个属性来获取旋转相关信息:(1)、@property (nonatomic)CGFloat rotation;获取旋转角度;
(2)、@property (nonatomic,readonly) CGFloat velocity;获取旋转速度;
实例:通过旋转手势旋转图片
- (void)viewDidLoad { [super viewDidLoad]; srcImage = [UIImage imageNamed:@"妞.png"]; //设置图片直接显示在中间(不进行任何缩放) self.view.backgroundColor = [UIColor lightGrayColor]; gv = [[UIImageView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; currentSize = CGSizeMake(self.view.frame.size.width - 20, 200); gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; gv.image = srcImage; //创建UIPinchGestureRecognizer手势处理器,该手势处理器激发scaleImage:方法 UIRotationGestureRecognizer* gesture = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotateImage:)]; [gv addGestureRecognizer:gesture]; } - (void)rotateImage:(UIRotationGestureRecognizer *)gesture{ //根据当前缩放的比例计算缩放后的目标图片大小 gv.transform = CGAffineTransformMakeRotation(imageRotate + gesture.rotation); if (gesture.state == UIGestureRecognizerStateEnded) { imageRotate += gesture.rotation; } }
4、使用UISwipeGestureRecognizer处理轻扫手势
UISwipeGestureRecognizer定义了如下两个属性来设置相关信息:(1)、@property(nonatomic) NSUInteger numberOfTouchesRequired;指定该手势处理器只处理几个手指的触碰事件;
(2)、@property(nonatomic) UISwipeGestureRecognizerDirection direction;指定该手势处理器处理该方向的轻扫.该属性支持:
typedef NS_OPTIONS(NSUInteger, UISwipeGestureRecognizerDirection) { UISwipeGestureRecognizerDirectionRight = 1 << 0, UISwipeGestureRecognizerDirectionLeft = 1 << 1, UISwipeGestureRecognizerDirectionUp = 1 << 2, UISwipeGestureRecognizerDirectionDown = 1 << 3 };
实例:
- (void)viewDidLoad { [super viewDidLoad]; //设置图片直接显示在中间(不进行任何缩放) self.view.backgroundColor = [UIColor lightGrayColor]; gv = [[UIImageView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; label = [[UILabel alloc]initWithFrame:CGRectMake(20, 50, gv.frame.size.width - 40, 80)]; label.numberOfLines = 2; [gv addSubview:label]; for (int i = 0 ; i < 4; i ++) { //创建手势处理器,指定使用该控制器的handleSwipe:方法处理轻扫手势 UISwipeGestureRecognizer* gesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipe:)]; //设置该手势处理器只处理i个手指的轻扫手势 gesture.numberOfTouchesRequired = 1; //指定该手势处理器只处理1 << i方向的轻扫手势 gesture.direction = 1 << i; [gv addGestureRecognizer:gesture]; } } - (void)handleSwipe:(UISwipeGestureRecognizer *)gesture{ //获取手势的方向 NSUInteger direction = gesture.direction; //根据手势方向的值得到方向字符串 NSString* dirStr = direction == UISwipeGestureRecognizerDirectionRight?@"向右":(direction == UISwipeGestureRecognizerDirectionLeft?@"向左":(direction == UISwipeGestureRecognizerDirectionUp?@"向上":@"向下")); NSLog(@"dirStr = %@",dirStr); NSUInteger touchNum = gesture.numberOfTouchesRequired; label.text = [NSString stringWithFormat:@"用户使用%ld个手指进行轻扫,方向为%@",touchNum,dirStr]; //指定5秒后清除label的文本 [label performSelector:@selector(setText:) withObject:@"" afterDelay:5]; }
5、使用UIPanGestureRecognizer处理拖动手势
UIPanGestureRecognizer定义了如下的方法来获取拖动相关信息:(1)、@property (nonatomic) NSUInteger minimumNumberOfTouches;设置移动手势处理器最少需要几个手指拖动;
(2)、@property (nonatomic) NSUInteger maximumNumberOfTouches;设置移动手势处理器最多支持几个手指拖动;
(3)、- (CGPoint)translationInView:(UIView *)view;获取该拖动手势在指定控件上的位移.该方法返回一个CGPoint结构体数据,该结构体中x变量的值代表水平方向的位移,y变量的值代表了垂直方向的位移;
(4)、- (CGPoint)velocityInView:(UIView *)view; 获取该拖动手势在指定控件上的拖动速度.该方法返回一个CGPoint结构体数据,该结构体中x变量的值代表了水平方向的速度,y变量的值代表了垂直方向的速度.
例如:
- (void)viewDidLoad { [super viewDidLoad]; //设置图片直接显示在中间(不进行任何缩放) self.view.backgroundColor = [UIColor lightGrayColor]; gv = [[UIImageView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width - 20, 200)]; gv.layer.borderWidth = 2; gv.layer.cornerRadius = 6; //设置gv控件支持用户交互 gv.userInteractionEnabled = YES; //设置gv控件支持多点触碰 gv.multipleTouchEnabled = YES; [self.view addSubview:gv]; label = [[UILabel alloc]initWithFrame:CGRectMake(20, 50, gv.frame.size.width - 40, 80)]; label.numberOfLines = 3; [gv addSubview:label]; //创建手势处理器,指定使用该控制器的handlePan:方法处理手势 UIPanGestureRecognizer* gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePan:)]; //设置该手势处理器最少需要1个手指 gesture.minimumNumberOfTouches = 1; //设置该手势处理器最多需要2个手指 gesture.maximumNumberOfTouches = 2; [gv addGestureRecognizer:gesture]; } //实现手势处理器的方法,该方法应该生命一个形参 //当该方法被激发时,手势处理器会作为参数传给该方法的参数 - (void)handlePan:(UIPanGestureRecognizer *)gesture{ CGPoint velocity = [gesture velocityInView:gv]; CGPoint translation = [gesture translationInView:gv]; label.text = [NSString stringWithFormat:@"水平速度为:%g,垂直速度为:%g,水平距离为:%g,垂直距离为:%g",velocity.x,velocity.y,translation.x,translation.y]; }
6、使用UILongGestureRecognizer处理长按手势
UILongGestureRecognizer定义了如下属性来设置该手势处理器的相关信息:(1)、@property (nonatomic) CFTimeInterval minimumPressDuration;指定用户至少在屏幕上按下多少秒才会触发该长安手指,该属性默认值为0.5;
(2)、@property (nonatomic) NSUInteger numberOfTapsRequired;指定必须使用几个手指在屏幕上长按才会触发该手势;
(3)、@property (nonatomic) CGFloat allowableMovement;指定该长按手势允许用户移动手指的最大距离,如果用户手指按下时移动超过了该距离,则长按手势失效.
实例:
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightGrayColor]; //创建一个NSMutableArray集合 用于保存过个按钮 bnArray = [[NSMutableArray alloc]init]; //创建一个手势处理器 用于检测.处理长按手势 UILongPressGestureRecognizer* gesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; //为该控件添加手势处理器 [self.view addGestureRecognizer:gesture]; } - (void)longPress:(UILongPressGestureRecognizer *)gesture{ //创建一个按钮 UIButton* bn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; //获取NSArray中已经包含了几个按钮 NSInteger count = bnArray.count; //计算当前添加的按钮位于第几行第几列 NSInteger row = count/3; NSInteger col = count%3; //为按钮设置文本 [bn setTitle:[NSString stringWithFormat:@"按钮%ld",bnIndex] forState:UIControlStateNormal]; //设置该bn按钮的大小和位置 bn.frame = CGRectMake(MARGINE + col * CELL_WIDTH, row * CELL_HEIGHT + MARGINE, BUTTON_WIDTH, BUTTON_HEIHGT); //为该按钮添加事件处理方法 [bn addTarget:self action:@selector(remove:) forControlEvents:UIControlEventTouchUpInside]; [bnArray addObject:bn]; //将按钮添加到应用界面的UIView控件中 [self.view addSubview:bn]; bnIndex ++; } - (void)remove:(id)sender{ //删除事件源控件(激发该事件的按钮) [sender removeFromSuperview]; //将触发该事件的按钮从NSMutableArray集合中删除 [bnArray removeObject:sender]; [self rearrange]; } - (void)rearrange{ //重新计算每个按钮的大小和位置 for (int i = 0; i < bnArray.count ; i ++) { NSInteger row = i/3; NSInteger col = i%3; UIButton* bn = [bnArray objectAtIndex:i]; bn.frame = CGRectMake(MARGINE + col * CELL_WIDTH, row * CELL_HEIGHT + MARGINE, BUTTON_WIDTH, BUTTON_HEIHGT); } }
四、创建和使用自定义手势处理器
1、开发自定义手势处理器
开发自定义手势处理器的步骤比较简单,按如下步骤操作即可:①、创建继承UIGestureRecognizer的子类;
②、重写UIGestureRecognizer基类的触碰相关的4个方法.通过在这些方法中识别用户手指划过的痕迹—当用户手指划过的痕迹符合手势要求时,程序将该手势state设为UIGestureRecognizerStateEnaded即可.
2、使用自定义手势处理器
待定五、手势互斥
手势识别是具有互斥的原则的,比如单击和双击,如果它识别出一种手势,其后的手势将不被识别。所以对于关联手势,要做特殊处理以帮助程序甄别,应该把当前手势归结到哪一类手势里面。比如,单击和双击并存时,如果不做处理,它就只能发送出单击的消息。为了能够识别出双击手势,就需要做一个特殊处理逻辑,即先判断手势是否是双击,在双击失效的情况下作为单击手势处理。使用
[A requireGestureRecognizerToFail:B]函数,它可以指定当A手势发生时,即便A已经滿足条件了,也不会立刻触发,会等到指定的手势B确定失败之后才触发。
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer; //例如 [singleRecognizer requireGestureRecognizerToFail:doubleRecognizer];
六、UIGestureRecognizerDelegate
1、// called when a gesture recognizer attempts to transition out of UIGestureRecognizerStatePossible. returning NO causes it to transition to UIGestureRecognizerStateFailed - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
手势可能发生的条件,比如某些特殊情况下,不想让此手势发生,就return NO。
2、
// called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other // return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously) // // note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
gestureRecognizer可能被otherGestureRecognizer手势识别引起阻塞,返回YES为允许同时识别。默认返回NO(默认情况下同一种手势只能识别一个)
3、
// called before touchesBegan:withEvent: is called on the gesture recognizer for a new touch. return NO to prevent the gesture recognizer from seeing this touch - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
方法touchesBegan:withEvent:执行前将调用该方法,返回YES执行手势事件;返回NO,将不响应手势事件。
相关文章推荐
- PHP手册-语言参考-基本语法-从HTML中分离
- windows删除node_modules 文件名或扩展名太长
- TP框架数据库的查询及添加
- oracle触发器,一个表新增、修改的同时同步另一张表
- a5d27 第1级bootloader启动问题
- 飞思卡尔 K20 CAN FIFO简单笔记
- [疯狂Java]正则表达式:捕获组、反向引用、捕获组命名
- web基础篇_笔记
- 微信对接
- PHP实例化对象
- Regex
- Ruby+watir自动化测试中实现识别验证码图片
- cas server 端配置+2客户端配置
- Tomcat是如何启动及运行—对tomcat的源码解析
- JSONObjec的应用
- TestNG 集成Spring-test
- Android开发基础复习zhi2
- LeetCode刷题系列(十一)Data Structure
- iOS coredata的使用及版本升级
- CSS 下拉菜单