:多点触摸与手势检测
Posted xiaoxiaobukuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:多点触摸与手势检测相关的知识,希望对你有一定的参考价值。
一、响应者链
只要继承了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 == [email protected]"向右":(direction == [email protected]"向左":(direction == [email protected]"向上":@"向下"));
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,将不响应手势事件。
以上是关于:多点触摸与手势检测的主要内容,如果未能解决你的问题,请参考以下文章