Canoe

iOS核心动画实践六(隐式动画)

2017.06.21

隐式动画就是我们在控制layer的属性变化的时候,不需要自己去设置动画,CoreAnimation会自动分配动画,平滑过渡到新的值。
那么隐式动画是如何控制的呢?动画执行的时间取决于当前动画事务的设置,动画类型取决于layer的行为。
动画事务由CATransaction来配置。

- (IBAction)changeColor
{
    //begin a new transaction
    [CATransaction begin];
    //set the animation duration to 1 second
    [CATransaction setAnimationDuration:1.0];
    //randomize the layer background color
    CGFloat red = arc4random() / (CGFloat)INT_MAX;
    CGFloat green = arc4random() / (CGFloat)INT_MAX;
    CGFloat blue = arc4random() / (CGFloat)INT_MAX;
    self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    //commit the transaction
    [CATransaction commit];
}

控制隐性动画的时间为1秒。

图层行为

我们直接改变View的属性或者View对应的Layer的属性,发现并不会像直接改变Layer一样有隐式动画。
Layer隐式动画调用的流程:

  1. 修改Layer的属性,调用-actionForkey:方法,传递属性的名称。
  2. 检测Layer是否有delegate,并且delegate实现-actionForlayer:forKey方法,如果有直接调用并返回结果。
  3. 没有delegte或者deleagte没有实现代理方法,接着检查actions字典,查找修改属性的对应行为。
  4. 如果actions字典没有找到对应的属性,那么Layer继续在Style字典中查找。
  5. 如果在Style中也没有找到,那么直接调用定义了每个属性标准行为的-defaultActionForKey:方法。

从这里我们可以看出View为什么可以禁用隐式动画,它实现了-actionForlayer:forKey方法并且在这个方法中返回nil。

除了返回nil之外还有一种方式设置禁止隐式动画。
[CATransaction setDisableActions:YES];

小结:对于View,我们可以使用UIView的动画函数,或者继承UIView重写-actionForlayer:forKey:方法,或者直接创建一个显式动画。对于Layer,可以设置代理然后实现-actionForlayer:forKey:控制隐式动画,或者提供一个actions字典来控制隐式动画。

//设置layer的改变背景颜色动画从左往右推进
    self.colorLayer = [CALayer layer];
    self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
    self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add a custom action
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromLeft;
    self.colorLayer.actions = @{@"backgroundColor": transition};
    //add it to our view
    [self.layerView.layer addSublayer:self.colorLayer];

呈现与模型

当改变一个图层的属性的时候,属性的值是立即更新的,但是屏幕上并没有马上发生改变,实际上是在定义动画事务结束之后图层如何显示的模型。

-presentationLayer通过这个方法可以访问正处于动画状态过程中的layer,他是动画layer的复制。
一般在两种情况下我们会用到presentationLayer

  • 基于定时器的动画,这个时候对于某一个时刻的图层的位置和变化就很有用。
  • 和用户交互,可以使用-hitTest:方法判断指定图层是否被触摸,这个时候判断呈现的图层会更加有意义。
//get the touch point
    CGPoint point = [[touches anyObject] locationInView:self.view];
    //check if we've tapped the moving layer
    if ([self.colorLayer.presentationLayer hitTest:point]) {
        //randomize the layer background color
        CGFloat red = arc4random() / (CGFloat)INT_MAX;
        CGFloat green = arc4random() / (CGFloat)INT_MAX;
        CGFloat blue = arc4random() / (CGFloat)INT_MAX;
        self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    } else {
        //otherwise (slowly) move the layer to new position
        [CATransaction begin];
        [CATransaction setAnimationDuration:4.0];
        self.colorLayer.position = point;
        [CATransaction commit];
    } 
Comments
Write a Comment