iOS中UIGestureRecognizer相关问题

作者:代培
地址:http://blog.csdn.net/dp948080952/article/details/52871632
转载请注明出处

前言

这段时间在写一些手势相关的代码,遇到了不少的坑,也解决了不少问题,所以准备写这篇博客,将自己的这段时间遇到的东西总结出来分享给大家,当然需要注意的是这篇文章并不是介绍如何使用UIGestureRecognizer,因为这类文章已经非常多了,我再写一篇也没有多大意义,也很难写出跟别人不一样的文章,这篇文章重点在于手势中的一些问题。

正文

UIPinchGestureRecognizer的缩放中心

当我们使用使用系统的图库等软件时,我们会发现使用两个手指缩放图片的时候,你会发现图片是完全跟着手的,即无论你的手指如何移动,图片上与你手指重合的点永远是不变的,而当你使用Pinch手势时其实缩放的中心不是一个固定的点,这个点是在两个手指的连线上,而当你一个手指固定不动,另一个手指移动,此时的缩放中心是在你不动的那个手指上。
而目前这种效果我还未想到如何使用UIPinchGestureRecognizer去实现这个效果,因为UIPinchGestureRecognizer只提供了缩放的比例Scale和location即是两指的中心,而使用这两个数据达到上面的效果应该是不可能的,所以我觉得苹果应该没有使用UIPinchGestureRecognizer,而是直接对UITouch进行处理。
而这里我想说的是如何使用UIPinchGestureRecognizer实现图片的放大中心为两手指的中心。当我们在GestureRecognizer的delegate中改变UIView的transform属性时,archpoint为UIView的center,这个时候我们就需要做点处理。

CGAffineTransform transform = CGAffineTransformScale(activeView.transform, sender.scale, sender.scale);
    activeView.transform = transform;
[sender setScale:1.0f];    

这是利用pinch手势缩放一个view的标准的代码,但是这样写的化,缩放中心默认就是在View的中心,当View被放大的很大以后,这样的体验是很差的。

我画了一张图,来说明如何将缩放中心从中心移到两个手指的中心,图中的黑色小正方形是原始的View,黑色大正方形是以View的center为缩放中心放大两倍后的位置,红色大正方形是以正方形左上角为缩放中心放大两倍后的位置,我们注意他们center的距离是Δl,重点是如何通过计算得到Δl并将其center移动Δl。
首先得缩放前到archpoint到center的距离l1,然后得到缩放后archpoint到center的距离l2,l2剪l1便是Δl,我们将其分解到x和y方向,于是就有下面的代码:

CGPoint pinchCenter = [sender locationInView:activeView.superview];
    CGFloat distanceX = activeView.center.x - pinchCenter.x;
    CGFloat distanceY = activeView.center.y - pinchCenter.y;
    CGFloat scaledDistanceX = distanceX * sender.scale;
    CGFloat scaledDistanceY = distanceY * sender.scale;
    activeView.center = CGPointMake(activeView.center.x + scaledDistanceX - distanceX, activeView.center.y + scaledDistanceY - distanceY);

如此便可实现缩放中心为两手指之间的缩放了,大家要想看详细代码,我在文章结束会放上demo的地址,可供大家下载。

UIPanGestureRecognizer的失灵问题

在项目中曾经遇到过一个很奇怪的问题,如下图在一个VC中有一个表情,这个表情可以移动旋转缩放,而当把它放大到很大的情况下,你会发现移动这个表情变得异常艰难,当你在屏幕上移动很长时间一段距离以后,这个表情才开始随着你的手移动。

刚开始我以为是UIPanGestureRecognizer的bug,但后来发现不是这样,也不可能是这样,经过一番研究我发现一个规律当这个表情被放的越大,就需要经过越长的距离才能启动pan手势,很明显UIPanGestureRecognizer是有一个启动的距离,而这个距离跟View的scale有关系,当这个scale越大,启动距离就越大。为什么会有这样的情况,是因为transform的变化的过程中frame是在变化的,而bounds却没有发生变化。所以假设上图这个表情所在的View的width:100 height:100,当缩放为以前的两倍后,frame的size变为:(200, 200),而bounds仍然为:(100, 100),所以当你的手指划过整个View的过程中,以这个View为参考的话你只划过了100,而对于父View来说你划过了200。
所以UIPanGestureRecognizer的失灵问题也就很好解释了,pan手势能被识别是需要一定距离的,而且对于检测手势的View来说是相对距离,当View被放大后,这个相对距离所换算的绝对距离就会很大,导致UIPanGestureRecognizer失灵。
下面说说解决方案,解决方案有两种:

  1. 将检测手势的工作交给父View,如此一来不管子View放得多大,都没有问题
  2. 如果放在父View中害怕手势冲突,也有解决方法,就是不使用UIPanGestureRecognizer,直接监控UITouch。

具体如何解决可以到文章结尾下我的代码。

多手势同时处理

在默认的情况下,当一个手势成功被检测到以后,其他手势就会被阻止,比如你在pinch缩放的同时想要rotation旋转,rotation却无法被识别,这时就可以用到UIGestureRecognizer的代理方法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

当一个手势被识别后,同时又有一个手势达到触发条件时,就会调用这个方法,已经被识别的手势是第一个参数,刚达到触发条件的为第二个参数,返回YES即可以处理第二个手势,返回NO不处理第二个手势。

手势冲突的简单解决方案

这里我介绍的是一个相对特殊的一个情景,也就是上面情景的延续,上面用父View检测pan手势,并移动表情,而现在还有加一个scrollview,也是检测pan手势,那如何解决这冲突呢?当然要先从情景来分析,这里同样是pan手势,但是触发条件是不同的,当起始点在表情内部,那么触发移动表情的pan手势,如果起始点在表情外部,触发scrollview的pan手势,但最开始我处理的方式还是有点傻,我重写了- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event这个方法,检测touch的起始位置,来决定启动启动哪一个pan手势,后来这样不好,有很多弊端,而UIGestureRecognizer的代理方法中就已经提供了很好的解决方法:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

在这个方法中获取gestureRecognizer的位置,决定是否开始这个gestureRecognizer,很简单也很好用。

总结

其实还有一个问题还没有解决就是rotation旋转中心的问题,这个问题我还会继续探索,一旦找到解决方案,一定会在博客中分享出来。

代码地址:https://github.com/948080952/GestureDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值