首页 > 代码库 > iOS 为移动动画中的View添加touch事件

iOS 为移动动画中的View添加touch事件

对Core Animation来说,不管是显式动画还是隐式动画,对其设置frame都是立即设置的,比如说给一个UIView做移动动画,虽然看起来frame在持续改变,但其时它的frame已经是最终值了,这种情况下,哪怕这个UIView是UIButton的实例,其触发touch事件的范围还是最终frame的地方。比如一个Button的frame是(0,0,100,100),要把它从0,0移动到200,200,在这种情况下:
  1. 如果你使用的是显式动画(CAKeyframeAnimation和CABasicAnimation),是通过指定path或values来进行动画的,它的frame并没有改变,touch范围还是(0,0,100,100)这个范围内
  2. 如果你使用的是隐式动画,是通过设置frame来进行动画的,那么它的touch范围就是(200,200,100,100)这个范围内
如果我们要为移动动画中的View添加touch事件,就需要在后面用到hitTest。我们先创建一个动画Layer:
CGSize layerSize = CGSizeMake(100, 100);
CALayer *movingLayer = [CALayer layer];
movingLayer.bounds = CGRectMake(0, 0, layerSize.width, layerSize.height);
[movingLayer setBackgroundColor:[UIColor orangeColor].CGColor];
movingLayer.anchorPoint = CGPointMake(0, 0);
[self.view.layer addSublayer:movingLayer];
self.movingLayer = movingLayer;
这里面只有anchorPoint重要一些,因为anchorPoint能影响position的取值,对Layer来说,frame是抽象的,只有bounds和position是真实存在的,并且设置frame和设置anchorPoint的顺序不同,开始看到的结果也不同:

先设置anchorPoint开始就能得到正确的结果,即position为0,0,如果设置bounds的话就没那么麻烦,顺序就无所谓了。之所以要设置anchorPoint,是因为后面的CAKeyframeAnimation将用position来做动画,如果anchorPoint为默认值(即0.5,0.5)的话,将会出现一个x、y轴一半的偏移,一张图解释anchorPoint的取值:

position是以anchorPoint为原点的,默认这个原点在中心位置,自然就会出现x、y轴的偏移,其实只要我在动画中将position做个偏移就不用设置anchorPoint,但是我觉得动画从0到终点更直观一些,所以这才是设置anchorPoint的原因。最后用一个weak属性引用动画Layer并把这个Layer添加到self.view.layer中。
接下来是动画部分:
CAKeyframeAnimation *moveLayerAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//moveLayerAnimation.fromValue = http://www.mamicode.com/[NSValue valueWithCGPoint:CGPointMake(0, 0)];>如果是用CABasicAnimation做动画,则用fromValue及toValue替换setValues,timingFunction直接用线性,不用做其他变换,关于这个属性的预置值,我在另一篇博文中有提到。
接下来为self.view添加手势识别:
........
    self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(click:)];
    [self.view addGestureRecognizer:self.tapGesture];
}

-(void)click:(UITapGestureRecognizer *)tapGesture {
    CGPoint touchPoint = [tapGesture locationInView:self.view];
    if ([self.movingLayer.presentationLayer hitTest:touchPoint]) {
        NSLog(@"presentationLayer");
    }
}
我在最开始的时候有提到,动画的过程只是看起来是动态变换的,其内部的值已经是固定的了,在这种情况下,Layer内部会通过复制一个当前Layer的副本来展示动画过程,而我们可以通过访问Layer的presentationLayer属性来得到这个副本的副本,通过presentationLayer我们可以知道当前动画已经进行到了屏幕的哪个位置上了,再直接通过hitTest来判定是不是一次有效点击即可。