阅读

iOS开发笔记之多点触控

编者按: 多点触控乃苹果公司带给世界的创新之首,作为移动开发者,熟练掌握多点触控开发技能很有必要。



一、处理触摸的四个方法:

//首次在屏幕上检测到触摸时调用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesBegan");
}

//如果触摸移动到了新的位置则会调用此方法
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesMoved");
}

//当触摸离开屏幕调用此方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesEnded");
}

//如系统决定取消此次触摸,那可能就不调用touchesEnded方法了,在这种情况下会调用touchesCancelled方法
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesCancelled");
}

快速点击屏幕,Debug窗口显示

2014-01-13 22:47:10.121 bbsTouch[593:70b] touchesBegan

2014-01-13 22:47:10.125 bbsTouch[593:70b] touchesEnded

2014-01-13 22:47:10.238 bbsTouch[593:70b] touchesBegan

2014-01-13 22:47:10.239 bbsTouch[593:70b] touchesEnded

2014-01-13 22:47:10.242 bbsTouch[593:70b] touchesBegan

2014-01-13 22:47:10.244 bbsTouch[593:70b] touchesEnded

点击并在屏幕上拖动,Debug窗口显示

2014-01-13 22:48:44.148 bbsTouch[593:70b] touchesBegan

2014-01-13 22:48:44.163 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.195 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.211 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.229 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.249 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.281 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.314 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.330 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.347 bbsTouch[593:70b] touchesMoved

2014-01-13 22:48:44.821 bbsTouch[593:70b] touchesEnded

touchesMoved可能不会在触摸队列中产生,然而,touchesBegan事件之后,总会产生touchesEnded或touchesCancelled事件。

二、开启多点触控的方法:

若在真机设备构建和运行应用程序,就会发现虽然在屏幕上按下了两根手指,但第二个触点却被忽略了。这是因为,默认情况下View是忽略多触点的,必须专门为需要支持多触点的View开启此功能。若要开启多触点,需要编写代码修改根视图的multipleToucheEnabled属性。

self.view.multipleTouchEnabled = TRUE;

在触摸的生命周期里,每个UITouch对象都为同一个实例化对象,也就是说屏幕每个独立触摸全程都表示各为同一UITouch对象。在触摸方法添加代码如下。

//首次在屏幕上检测到触摸时调用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesBegan");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}
}

//如果触摸移动到了新的位置则会调用此方法
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesMoved");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}

}

//当触摸离开屏幕调用此方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesEnded");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}

}

//如系统决定取消此次触摸,那可能就不调用touchesEnded方法了,在这种情况下会调用touchesCancelled方法
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesCancelled");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}

}

增加一段代码作用是打印出包含在集合中每个UITouch对象的内存地址。下面为设备屏幕按下两根手指的输出结果

2014-01-14 23:52:56.590 bbsTouch[3671:907] touchesBegan

2014-01-14 23:52:56.606 bbsTouch[3671:907] - 0x1ddaac00

2014-01-14 23:52:56.623 bbsTouch[3671:907] touchesBegan

2014-01-14 23:52:56.628 bbsTouch[3671:907] - 0x1dd9b690

2014-01-14 23:52:56.633 bbsTouch[3671:907] touchesMoved

2014-01-14 23:52:56.637 bbsTouch[3671:907] - 0x1ddaac00

2014-01-14 23:52:56.895 bbsTouch[3671:907] touchesMoved

2014-01-14 23:52:56.902 bbsTouch[3671:907] - 0x1dd9b690

2014-01-14 23:52:56.904 bbsTouch[3671:907] - 0x1ddaac00

2014-01-14 23:52:56.958 bbsTouch[3671:907] touchesEnded

2014-01-14 23:52:56.963 bbsTouch[3671:907] - 0x1dd9b690

2014-01-14 23:52:56.967 bbsTouch[3671:907] - 0x1ddaac00

两根手指先后触摸屏幕,进入touchesBegan,内存分为0x1ddaac00,0x1dd9b690触摸过程完成后,内存地址也能被系统回收。

三、调用UITouch对象方法——locationInView,多点移动多个对象

屏幕上新建两个view,实现两个手指在屏幕上移动两个view,通过规则约束两个view的活动范围。

关键在于调用UITouch对象的方法——lacationInView。此方法返回View的相对于根视图的触摸位置。返回值是一个CGPoint类型,是一个包含X坐标和Y坐标的结构体。我让两个view在屏幕上下两个半区水平移动。利用CGPointMake来快速初始化新的CGPoint结构体。

- (void)viewDidLoad
{
[super viewDidLoad];
self.view.multipleTouchEnabled = TRUE;
// Do any additional setup after loading the view, typically from a nib.
_view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 40, 40, 40)];
_view1.backgroundColor = [UIColor blueColor];
[self.view addSubview:_view1];

_view2 = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 60, 40, 40)];
_view2.backgroundColor = [UIColor yellowColor];
[self.view addSubview:_view2];

}

//首次在屏幕上检测到触摸时调用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesBegan");
for (UITouch *touch in touches)
{
// NSLog(@" - %p",touch);
//获取根视图内触摸点的point
CGPoint touchPoint = [touch locationInView:self.view];
//约束两个view的活动范围
if (touchPoint.y < self.view.frame.size.height/2)
{
_view1.center = CGPointMake(touchPoint.x, _view1.center.y);
}else
{
_view2.center = CGPointMake(touchPoint.x, _view2.center.y);
}
}
}

//如果触摸移动到了新的位置则会调用此方法
//以上对触摸进行了初始化,并未处理沿着屏幕移动的触摸。所以,只需要在touchesMoved方法里调用touchesBegan的处理方法来改写移动球拍的逻辑即可。
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesMoved");
for (UITouch *touch in touches)
{
// NSLog(@" - %p",touch);
[self touchesBegan:touches withEvent:event];
}

}

//当触摸离开屏幕调用此方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesEnded");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}

}

//如系统决定取消此次触摸,那可能就不调用touchesEnded方法了,在这种情况下会调用touchesCancelled方法
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesCancelled");
for (UITouch *touch in touches)
{
NSLog(@" - %p",touch);
}

}

下图为两个view可以分别沿着水平方向移动



四、可靠的多点触控,为每个View分配唯一触摸对象

每个View需要分配有效的触摸以避免第三方触摸的干扰。实现方法如下

.h文件,接口定义两个变量


#import <UIKit/UIKit.h>

@interface BBSViewController : UIViewController
{
UITouch *touch1;
UITouch *touch2;
}

.m文件,在touchesBegan里为view分配一个特定触摸对象(仅当它还未分配时)。


//首次在屏幕上检测到触摸时调用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesBegan");
for (UITouch *touch in touches)
{
// NSLog(@" - %p",touch);
//获取根视图内触摸点的point
CGPoint touchPoint = [touch locationInView:self.view];
//约束两个view的活动范围
if (touch1 == nil && touchPoint.y < self.view.frame.size.height/2)
{
touch1 = touch;
_view1.center = CGPointMake(touchPoint.x, _view1.center.y);
}else if (touch2 == nil && touchPoint.y > self.view.frame.size.height/2)
{
touch2 = touch;
_view2.center = CGPointMake(touchPoint.x, _view2.center.y);
}
}
}

在touchesMoved方法里,忽略所有未绑定View的触摸

//如果触摸移动到了新的位置则会调用此方法
//以上对触摸进行了初始化,并未处理沿着屏幕移动的触摸。所以,只需要在touchesMoved方法里调用touchesBegan的处理方法来改写移动球拍的逻辑即可。
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesMoved");
for (UITouch *touch in touches)
{
// NSLog(@" - %p",touch);
// [self touchesBegan:touches withEvent:event];
CGPoint touchPoint = [touch locationInView:self.view];

if (touch == touch1)
{
_view1.center = CGPointMake(touchPoint.x, _view1.center.y);
}
else if (touch == touch2)
{
_view2.center = CGPointMake(touchPoint.x, _view2.center.y);
}
}
}

及时释放已绑定了View的触摸,避免手指一离开屏幕就永久失去对View的控制。

//当触摸离开屏幕调用此方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesEnded");
for (UITouch *touchin touches)
{
NSLog(@" - %p",touch);
if (touch == touch1)touch1 = nil;
else if (touch ==touch2) touch2 = nil;
}

}

//如系统决定取消此次触摸,那可能就不调用touchesEnded方法了,在这种情况下会调用touchesCancelled方法
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touchesCancelled");
// for (UITouch *touch in touches)
// {
// NSLog(@" - %p",touch);
// }
[self touchesEnded:touches withEvent:event];
}

这样多余的触摸将不会影响现有的View的位置。


鼓励一下

如果觉得我的文章对您有用,请土豪扫右侧二维码打赏2块钱,帮我买杯咖啡,您的支持将鼓励我继续创作!”


关于Kovli Studio

Kovli Studio是本人全栈开发历程中部分作品的展示平台。作品从衣食住行娱乐等各角度为用户提供实用、高品质、高颜值、高性价比的品质生活方案,欢迎使用。