问题情景的引入:在一个画板程序中,我定义只能用一个手指的时候,才可以画线,当有两个手指的时候是不可以进行绘画的。
问题初解决:
①那么最简单的我们会想到,禁止多点触摸不就可以了吗?
答:不行。假如我们禁止了多点触摸,那么当我们两个手指一起触摸的时候,触摸事件的委托方法
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
就会随机选一个触摸返回。也就是说始终是会返回一个触摸( touches. count = 1 ),那么照样还是可以进行画线。
②那么我们运行多点触摸,然后在触摸事件的委托方法中,判断触摸点的个数(touches.count),当只有 touches.count == 1 的时候,我们才允许画线不就可以了吗?
答:只有当两个手指同时进行触摸的时候,才可以。为什么呢?
首先,我们两个手指很有可能是一先一后触摸到屏幕的,
我们分析一下这个先后触摸的过程:当第一个手指触摸屏幕的时候,- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event 的委托方法会响应一次,这个时候的触摸个数为 1;
当第二个手指触摸屏幕的时候,- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event 这个方法再一次响应,这个时候的触摸个数为2.
那么问题就出现了,我们是允许触摸点为 1 的时候可以画线,大于 1 的时候不可以画线。那么由于两个手指先后触摸会有一个很小的时间间隔,那么就会出现画出一小段线段的问题。这个应该可以理解吧!
所以说,只有当两个手指同时触摸屏幕时,禁止画线才有效。
问题呈现:先后触摸如何消除这个影响呢?
关于这个问题的本质,先后触摸之间会有一个时间差,我们就可以在这个时间差中进行判断,看看是否发生了先后触摸的双指触摸屏幕。
怎么解决呢?设置一个定时器。
首先,定时器的时长就是先后触摸的时间差,根据测试,约为 0.08 ~0.1 s
当第一个手指触摸,触摸委托方法 touchesBegan 响应的时候,开启定时器,然后在这个定时过程中判断,是否还有触摸响应,如果有,说明就是发生了先后触摸了;那么如果没有呢?没有就说明这是一个单指的触摸。
好了,弄懂了解决的策略,我们就可以代码实现了:
①注意要开启多点触摸
-(BOOL)isMultipleTouchEnabled {
return YES;
}
②定义两个辅助变量:
int touchNum;
BOOL canDrawLine;
③在touchesBegan触摸委托方法开启定时器:
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{
if (touches.count == 1)
{
[self performSelector:@selector(touchBegan:) withObject:touches afterDelay:0.08];
}
touchNum++;
}
定时器方法:
-(void)touchBegan:(NSSet*) touches
{
canDrawLine = false;
if (touchNum == 1) {
canDrawLine = true;
NSLog(@"single touch");
}
else
{
//重置
touchNum = 0;
canDrawLine = false;
NSLog(@"double touch one after another");
}
}
解说:
(1)首先注意到touchesBegan方法中,是当触摸点是 1 的时候才开启定时器。显然,只有当touchesBegan响应是单指,我们才要看看,是否还有另一个手指触摸造成先后触摸的双指触摸情况;当触摸点已经是两个了,那么显然就不用这么麻烦啦,直接就知道了,所有就没有必要再开个定时器去判断了。
(2)每一次响应touchesBegan 都要对触摸次数进行计数。
(3)其实所谓定时器方法,就是一个延时执行的方法,那么在这个延时过程中就进行了我们的先后触摸的判断检测了。在这个方法中,我们根据touchNum进行判断touchesBegan方法响应了多少次,如果是单指触摸,那么肯定是只有1次,所以就可以画线了,否则就不能画线了。
④ touchesMoved 和 touchesEnded 对两个辅助变量的使用和设置
- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{
if (touches.count >= 2)
{
touchNum = 0;
canDrawLine = false;
return;
}
if ( canDrawLine )
{
}
}
- (void) touchesEnded:(NSSet *) touches withEvent:(UIEvent *) event
{
canDrawLine = false;
touchNum = 0;
}
这样就可以在画线方法中使用 canDrawLine 这个变量判别是否可以进行画线了。
⑤存在一个小小的缺陷,即使你是单指触摸进行画线,那么会有一个延时时间,就是定时器设置的判断延时时间。当然这个时间很小,假如不是在很快速的进行画线的情况下,是没有什么问题的。
问题再深入:上面介绍的这个方法,其实很通用,凡是涉及到的触摸过程有先后的话,都可以进行这样定时器的处理。在 cocos2dx 的单击双击检测中也是可以用到这种解决策略,请参看:点击打开链接
那么针对这个问题,我思考了一下,发现其实在IOS的使用手势处理单击,双击的问题处理上,用到过一个 requireGestureRecognizerToFail 方法,也就是说只有双击手势的响应检测失败后,才响应单击的手势。
而在iOS中,有关的滑动手势,涉及到两个:pan 和 swipe,二者的区别是pan是比较慢的滑动,而swipe是比较快速的滑动,而且在这两个方法中又是可以指定触摸的点数,那么结合requireGestureRecognizerToFail方法,是否可以创建两个有关的手势来进行判断呢?
经过实践。OK,是可以的,使用到的手势是pan。(swipe不行!)
注意也是要开启多点触摸的。
①添加一个辅助变量:
BOOL canDrawWithGesture;
注意为了提高单指触摸的响应,我们初始化为 true。
②创建两个手势:
UIPanGestureRecognizer *singleFingerOne = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(singleTouch)];
singleFingerOne.minimumNumberOfTouches = 1;
singleFingerOne.maximumNumberOfTouches = 1;
UIPanGestureRecognizer *doubleFingerOne = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(doubleTouch)];
doubleFingerOne.minimumNumberOfTouches = 2;
doubleFingerOne.maximumNumberOfTouches = 2;
[self addGestureRecognizer:singleFingerOne];
[self addGestureRecognizer:doubleFingerOne];
-(void)singleTouch
{
canDrawWithGesture = true;
// NSLog(@"single");
}
-(void)doubleTouch
{
canDrawWithGesture = false;
// NSLog(@"double");
}
③触摸委托方法中直接使用 canDrawWithGesture 变量进行判断就可以了
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{
if (canDrawWithGesture)
{
}
}
- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{
if ( canDrawWithGesture )
{
}
}
// Send over new trace when the touch ends
- (void) touchesEnded:(NSSet *) touches withEvent:(UIEvent *) event
{
canDrawWithGesture = true;
}
⑤其实,比较这两个解决方法,我觉得二者是有相似之处的,都是先探测是否出现了双指的触摸。而且我猜测,requireGestureRecognizerToFail 方法对不同手势的优先响应处理估计也是使用定时器的方法来实现的,因为每一个touch 都会有一个时间戳,这样进行定时器检测应该是可以实现的。--- 纯属个人猜测哈!
好了,关于这个触摸事件之双指先后触摸问题就讲述完了,关于在这个实际的问题提供的两种解决中,都可以满足要求,具体选择哪一种,再比较实际运行和其他因素。