UITouch && UIGestureRecognizer

Posted Jk_Chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UITouch && UIGestureRecognizer相关的知识,希望对你有一定的参考价值。

UITouch:(代表触摸对象的类)
····从UIView中重写以下方法响应触摸事件捕捉触摸对象:(若不重写,该UIView不会响应触摸事件)
参数touchse表示触摸产生的所有UITouch对象
参数event表示特定事件(包含整个触摸过程所有触摸对象,通过allTouches方法获取事件内所有触摸对象,touchesForView:或touchesForWindows:方法取出特定视图或窗口的触摸对象,然后根据触摸对象位置,状态,时间等属性做处理)
···· -(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;//如触摸过程中被来电打断
···· 图片移动范例:
···· //在UIViewController中重写方法
···· -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
···· UITouch *touch=[touches anyObject];
···· UIImageView *view1=(UIImageView *)[self.view viewWithTag:100];
···· CGPoint point=[touch locationInView:self.view];
···· CGRect frame=view1.frame;
···· frame.origin=point;
···· view1.frame=frame;
···· }
介绍:
触摸屏幕期间直至所有手指离开屏幕,所有的UITouch对象都被包含在UIEvent事件对象中,事件记录这周期中所有触摸对象状态的变化。
管理程序UIApplication对象将触摸事件分发,正常先发到主窗口,传给第一响应者对象处理。
响应者对象:可以响应时间并对时间做出处理,就是UIResponder类(子类有UIApplication,UIView等)
第一响应者:响应链的开端,当前接受触摸的响应者对象,表示当前该对象正与用户交互
响应者链:表示一系列响应者对象,事件交由第一响应者对象处理,若不处理,事件沿着响应者链向上传递,正常第一响应者是视图对象或其子类对象,
若不处理,事件交由视图控制器对象,然后是父视图对象,直到顶层视图——》窗口——》程序UIApplication——》丢弃
管理事件分发:视图对象可通过属性userInteractionEnabled控制释放需要作出事件回应,默认YES,当视图hidden为YES或alpha为0会不接收触摸事件处理。
默认视图是否接收多点触摸属性multipleTouchEnabled为NO。


主要属性:
window:触摸产生时所处的窗口。由于窗口可能发生变化,当前所在的窗口不一定是最开始的窗口。
view:触摸产生时所处的视图。由于视图可能发生变化,当前视图也不一定时最初的视图。
tapCount:轻击(Tap)操作和鼠标的单击操作类似,tapCount表示短时间内轻击屏幕的次数。因此可以根据tapCount判断单击、双击或更多的轻击。
timestamp:时间戳记录了触摸事件产生或变化时的时间。单位是秒。
phase:当前触摸事件所处触摸周期(开始,移动,结束,取消)。phase是UITouchPhase枚举型,包括:
UITouchPhaseBegan
UITouchPhaseMoved
UITouchPhaseStationary(触摸点无移动)
UITouchPhaseEnded
UITouchPhaseCancelled
主要方法:
-(CGPoint)locationInView:(UIView *)view;
返回触摸在针对view视图坐标上的位置,view参数为nil时,返回位置针对整个窗口的位置
-(CGPoint)previousLocationInView:(UIView *)view;
返回前一个坐标值,针对位置同上
实现手势实例:
轻扫:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
//kMinimumGestureLength 最小移动长度 kMaximumVariance最大偏移长度
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touchlocationInView:self.view];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
if(deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance){
label.text = @”Horizontal swipe detected”;
[self performSelector:@selector(eraseText)
withObject:nilafterDelay:2];
}else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance){
label.text = @”Vertical swipe detected”;
[selfperformSelector:@selector(eraseText)withObject:nilafterDelay:2];
}
}
捏合:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if([touches count] ==2){ //检测是否为两个手指触点
NSArray *twoTouches = [touchesallObjects];
UITouch *first = [twoTouchesobjectAtIndex:0];
UITouch *second = [twoTouchesobjectAtIndex:1];
initialDistance = distanceBetweenPoints(
[first locationInView:self.view], [secondlocationInView:self.view]);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if ([touches count] == 2){
NSArray *twoTouches = [touchesallObjects];
UITouch *first = [twoTouchesobjectAtIndex:0];
UITouch *second = [twoTouchesobjectAtIndex:1];
CGFloat currentDistance =distanceBetweenPoints(
[first locationInView:self.view],[secondlocationInView:self.view]);
if (initialDistance ==0){
initialDistance = currentDistance;//根据移动前后的坐标距离差检测是捏合的手势还是打开的手势
}else if (currentDistance - initialDistance > kMinimumPinchDelta){ //检测是否大于最小移动值kMinimumPinchDelta
label.text = @”Outward Pinch”;
[selfperformSelector:@selector(eraseLabel) withObject:nil afterDelay:1.6f];
}else if (initialDistance - currentDistance > kMinimumPinchDelta){
label.text = @”Inward Pinch”;
[selfperformSelector:@selector(eraseLabel) withObject:nil afterDelay:1.6f];
}
}
}


··可以通过处理touchesBegan/Moved/Ended/Cancelled做手势的判断,也可直接通过UIGestureRecognizer子类直接判断手势。

UIGestureRecognizer:(手势抽象类)
子类:(不同子类有其独特属性,但所有子类的触摸事件生命周期都一样并且所有子类用的都是同一套代理方法)
UITapGestureRecognizer(点击)
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTap:)];
tap.delegate=self;
tap.numberOfTapsRequired=1;
tap.numberOfTouchesRequired=1;
[myView addGestureRecognizer:tap];
-(void)handleTap:(UIGestureRecognizer *)gestureRecognizer{
UIView *view=gestureRecognizer.view;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // 正常情况下只响应这个消息
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
if (CGColorEqualToColor(view.backgroundColor.CGColor, [UIColor redColor].CGColor)) {
view.backgroundColor=[UIColor yellowColor];
}else{
view.backgroundColor=[UIColor redColor];
}
break;
}
case UIGestureRecognizerStateFailed:{
NSLog(@"======UIGestureRecognizerStateFailed");
break;
}
case UIGestureRecognizerStatePossible:{
NSLog(@"======UIGestureRecognizerStatePossible");
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}

UIPinchGestureRecognizer(捏合)
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate = self;
[myView addGestureRecognizer: pinch];
-(void)handlePinch:(UIPinchGestureRecognizer *)gestureRecognizer{
UIView *view = gestureRecognizer.view;
/*
scale属性:两手指之间的距离,是个比例值为***捏合过程中两手指之间距离:刚开始的两个手指之间的距离***,相对距离,不是绝对距离
velocity属性:两手指之间的移动速度,是个速度比例值为******,相对速度,不是绝对速度
以刚开始的两个手指对应的两个point的之间的距离为标准,此时velocity=0.
若两手指之间距离减小,则velocity为负数,从-0开始,随着手指向里捏合的速度越快,负值越大
若两手指之间距离变大,则velocity为正数,从0开始,随着手指向外捏合的速度越快,值越大
*/
CGFloat scale = gestureRecognizer.scale;
NSLog(@"======scale: %f", scale);
CGFloat velocity = gestureRecognizer.velocity;
NSLog(@"======velocity: %f", velocity);

/*
捏合手势
这个一般情况下只响应
UIGestureRecognizerStateBegan、
UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized、
UIGestureRecognizerStateChanged消息,
一个UIGestureRecognizerStateBegan,接下去是N多的UIGestureRecognizerStateChanged,scale的值此时会不断的变化,当手指离开时,响应UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized
*/
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
break;
}
case UIGestureRecognizerStateBegan:{
NSLog(@"======UIGestureRecognizerStateBegan");
break;
}
case UIGestureRecognizerStateChanged:{
NSLog(@"======UIGestureRecognizerStateChanged");
myView.transform = CGAffineTransformScale(myView.transform, gestureRecognizer.scale, gestureRecognizer.scale);
gestureRecognizer.scale = 1; // 重置,很重要
break;
}
case UIGestureRecognizerStateCancelled:{
NSLog(@"======UIGestureRecognizerStateCancelled");
break;
}
case UIGestureRecognizerStateFailed:{
NSLog(@"======UIGestureRecognizerStateFailed");
break;
}
case UIGestureRecognizerStatePossible:{
NSLog(@"======UIGestureRecognizerStatePossible");
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}

UIRotationGestureRecognizer(旋转)
UIRotationGestureRecognizer *rotation=[[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotation:)];
rotation.delegate = self;
[myView addGestureRecognizer:rotation];
-(void)handleRotation:(UIRotationGestureRecognizer *)gestureRecognizer{
UIView *view = gestureRecognizer.view;

/*
rotation属性:两手指之间的旋转的角度,是个比例,相对角度,不是绝对角度
以刚开始的两个手指对应的两个point的之间的那条直线为标准,此时rotation=1.
向顺时针旋转,则rotation为正数且不断变大,当旋转360度时,rotation大概为6左右,如果继续顺时针旋转,则角度会不断增加,两圈为12左右,此时若逆时针旋转,角度则不断变小
向逆时针旋转,则rotation为负数且不断变小,当旋转360度时,rotation大概为-6左右

velocity属性:两手指之间的移动速度,是个速度比例,相对速度,不是绝对速度
以刚开始的两个手指对应的两个point的之间的距离为标准,此时velocity=0.
若两手指向顺时针旋转,则velocity为正数,从0开始,随着手指向里捏合的速度越快,值越大,没有上限
若两手指向逆时针旋转,则velocity为负数数,没有上限,从-0开始,随着手指向外捏合的速度越快,值越小,没有上限
*/
CGFloat rotation = gestureRecognizer.rotation;
NSLog(@"===rotation: %f", rotation);

CGFloat velocity = gestureRecognizer.velocity;
NSLog(@"======velocity: %f", velocity);

/*
旋转手势
这个一般情况下只响应
UIGestureRecognizerStateBegan、
UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized、
UIGestureRecognizerStateChanged消息,
一个UIGestureRecognizerStateBegan,接下去是N多的UIGestureRecognizerStateChanged,scale的值此时会不断的变化,当手指离开时,响应UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized
*/
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
break;
}
case UIGestureRecognizerStateBegan:{
NSLog(@"======UIGestureRecognizerStateBegan");
break;
}
case UIGestureRecognizerStateChanged:{
NSLog(@"======UIGestureRecognizerStateChanged");
view.transform = CGAffineTransformRotate(view.transform, gestureRecognizer.rotation);
gestureRecognizer.rotation = 0; // 重置 这个相当重要
break;
}
case UIGestureRecognizerStateCancelled:{
NSLog(@"======UIGestureRecognizerStateCancelled");
break;
}
case UIGestureRecognizerStateFailed:{
NSLog(@"======UIGestureRecognizerStateFailed");
break;
}
case UIGestureRecognizerStatePossible:{
NSLog(@"======UIGestureRecognizerStatePossible");
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}

UISwipeGestureRecognizer(滑动,快速移动,用于监测滑动方向)
/*
同一个手势只能指定一个方向,不能同时指定多个方向,要指定多个方向 必须用多个手势
*/
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
swipeRight.delegate = self;
swipeRight.numberOfTouchesRequired = 1;//手指个数
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;//同一个手势只能指定一个方向,不能同时指定多个方向,要指定多个方向 必须用多个手势

UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
swipeLeft.delegate = self;
swipeLeft.numberOfTouchesRequired = 1;// 手指个数
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;// 同一个手势只能指定一个方向,不能同时指定多个方向,要指定多个方向 必须用多个手势

UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
swipeUp.delegate = self;
swipeUp.numberOfTouchesRequired = 1;// 手指个数 The default value is 1.
swipeUp.direction = UISwipeGestureRecognizerDirectionUp;// 同一个手势只能指定一个方向,不能同时指定多个方向,要指定多个方向 必须用多个手势

UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
swipeDown.delegate = self;
swipeDown.numberOfTouchesRequired = 1;// 手指个数 The default value is 1.
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;// 同一个手势只能指定一个方向,不能同时指定多个方向,要指定多个方向 必须用多个手势

[myView addGestureRecognizer:swipeRight];
[myView addGestureRecognizer:swipeLeft];
[myView addGestureRecognizer:swipeUp];
[myView addGestureRecognizer:swipeDown];
-(void)handleSwipe:(UISwipeGestureRecognizer *)gestureRecognizer{
UIView *view = gestureRecognizer.view;
/*
direction属性: 用来指明手势滑动的方向的。
*/
UISwipeGestureRecognizerDirection direction = gestureRecognizer.direction;
switch (direction) {
case UISwipeGestureRecognizerDirectionRight:
{
NSLog(@"direction==UISwipeGestureRecognizerDirectionRight");
break;
}
case UISwipeGestureRecognizerDirectionLeft:
{
NSLog(@"direction==UISwipeGestureRecognizerDirectionLeft");
break;
}
case UISwipeGestureRecognizerDirectionUp:
{
NSLog(@"direction==UISwipeGestureRecognizerDirectionUp");
break;
}
case UISwipeGestureRecognizerDirectionDown:
{
NSLog(@"direction==UISwipeGestureRecognizerDirectionDown");
break;
}
default:
break;
}
/*
轻扫手势
这个一般情况下只响应UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized
*/
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}
UIPanGestureRecognizer(拖移,慢速移动,用于监测偏移量)
UIPanGestureRecognizer *pan= [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
pan.delegate = self;
pan.maximumNumberOfTouches = NSUIntegerMax;
pan.minimumNumberOfTouches = 1;
[myView addGestureRecognizer:pan];
-(void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer{
UIView *view = gestureRecognizer.view;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:{
NSLog(@"======UIGestureRecognizerStateBegan");
break;
}
case UIGestureRecognizerStateChanged:{
NSLog(@"======UIGestureRecognizerStateChanged");
/*
让view跟着手指移动
1.获取每次系统捕获到的手指移动的偏移量translation
2.根据偏移量translation算出当前view应该出现的位置
3.设置view的新frame
4.将translation重置为0(十分重要。否则translation每次都会叠加,很快你的view就会移除屏幕!)
*/
CGPoint translation = [gestureRecognizer translationInView:self.view];
view.center = CGPointMake(view.center.x + translation.x, view.center.y + translation.y);
[gestureRecognizer setTranslation:CGPointMake(0, 0) inView:self.view];// 注意一旦你完成上述的移动,将translation重置为0十分重要。否则translation每次都会叠加,很快你的view就会移除屏幕!
break;
}
case UIGestureRecognizerStateCancelled:{
NSLog(@"======UIGestureRecognizerStateCancelled");
break;
}
case UIGestureRecognizerStateFailed:{
NSLog(@"======UIGestureRecognizerStateFailed");
break;
}
case UIGestureRecognizerStatePossible:{
NSLog(@"======UIGestureRecognizerStatePossible");
break;
}
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
/*
当手势结束后,view的减速缓冲效果
模拟减速写的一个很简单的方法。它遵循如下策略:
计算速度向量的长度(i.e. magnitude)
如果长度小于200,则减少基本速度,否则增加它。
基于速度和滑动因子计算终点
确定终点在视图边界内
让视图使用动画到达最终的静止点
使用“Ease out“动画参数,使运动速度随着时间降低
*/
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
CGPoint velocity = [gestureRecognizer velocityInView:self.view];// 分别得出x,y轴方向的速度向量长度(velocity代表按照当前速度,每秒可移动的像素个数,分xy轴两个方向)
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));// 根据直角三角形的算法算出综合速度向量长度
// 如果长度小于200,则减少基本速度,否则增加它。
CGFloat slideMult = magnitude / 200;
NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);
float slideFactor = 0.1 * slideMult; // Increase for more of a slide
// 基于速度和滑动因子计算终点
CGPoint finalPoint = CGPointMake(view.center.x + (velocity.x * slideFactor),view.center.y + (velocity.y * slideFactor));
// 确定终点在视图边界内
finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);
[UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
view.center = finalPoint;
} completion:nil];
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}
UILongPressGestureRecognizer(长按)
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.delegate = self;
longPress.numberOfTapsRequired = 0;
longPress.minimumPressDuration = 0.1f;
longPress.numberOfTouchesRequired = 1;
longPress.allowableMovement = 10;
[myView addGestureRecognizer:longPress];
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer{
UIView *view = gestureRecognizer.view;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateEnded:{ // UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
NSLog(@"======UIGestureRecognizerStateEnded || UIGestureRecognizerStateRecognized");
break;
}
case UIGestureRecognizerStateBegan:{
NSLog(@"======UIGestureRecognizerStateBegan");
break;
}
case UIGestureRecognizerStateChanged:{
NSLog(@"======UIGestureRecognizerStateChanged");
break;
}
case UIGestureRecognizerStateCancelled:{
NSLog(@"======UIGestureRecognizerStateCancelled");
break;
}
case UIGestureRecognizerStateFailed:{
NSLog(@"======UIGestureRecognizerStateFailed");
break;
}
case UIGestureRecognizerStatePossible:{
NSLog(@"======UIGestureRecognizerStatePossible");
break;
}
default:{
NSLog(@"======Unknow gestureRecognizer");
break;
}
}
}
手势识别是具有互斥的原则的,比如单击和双击,如果它识别出一种手势,其后的手势将不被识别。
所以对于关联手势,先判断手势属于什么类型,再做处理。
使用[A requireGestureRecognizerToFail:B]函数,可指定A手势发生时,不会立刻触发,等到手势B确定失败后才触发

UIGestureRecognizerDelegate:
// 询问一个手势接收者是否应该开始解释执行一个触摸接收事件
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
// CGPoint currentPoint = [gestureRecognizer locationInView:self.view];//针对整个屏幕坐标而非手势所在view
// NSLog(@"Current point :%f,%f",currentPoint.x,currentPoint.y);
// //让手势所针对视图只捕捉视图中的(0,0,100,100)范围的手势
// if (CGRectContainsPoint(CGRectMake(myView.frame.origin.x, myView.frame.origin.y, 100, 100), currentPoint) ) {
// return YES;
// }
// return NO;
return YES;
}

//返回两个手势是否同时接收消息(默认不同时接收),不同时接收(如果另外一个手势返回YES,则并不能保证不同时接收消息)
// 这个函数一般在一个手势接收者要阻止另外一个手势接收自己的消息的时候调用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return NO;
}

//是否允许手势接收者接收一个touch对象(是否响应触摸事件)
//这个方法在touchesBegan:withEvent:之前调用,为一个新的touch对象进行调用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}

以上是关于UITouch && UIGestureRecognizer的主要内容,如果未能解决你的问题,请参考以下文章

UIControl && UIImage

在移动 SKSpriteNode 时将 Velocity 添加到 SKPhysicsBody

SpriteKit:如何检测从节点外部开始的节点上的触摸

iOS开发UITouch触摸API简介

iOS开发UITouch触摸API简介

swift--触摸(UITouch)事件(点击,移动,抬起)