关于隐式动画的一些笔记
什么是隐式动画?
首先为 view 的根 layer 添加一个 subLayer
CALayer *testLayer = [CALayer layer];
testLayer.frame = CGRectMake(50, 50, 100, 100);
testLayer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:testLayer];
self.testLayer = testLayer;
然后尝试修改它的颜色
self.testLayer.backgroundColor = [UIColor blueColor].CGColor;
当改变了 CALayer
的一个可做动画的属性
它并不是立刻在屏幕上体现出来
它是从先前的值平滑过渡到新的值(从红色渐变到了蓝色)
这一切都是默认的行为,我们不需要做额外的操作。
这其实就是隐式动画
之所以叫隐式是因为我们并没有指定任何动画的类型
我们仅仅改变了一个属性
然后 Core Animation
来决定如何并且何时去做动画
当然 Core Animaiton
同样支持显式动画
但当改变一个属性 Core Animation
是如何判断动画类型和持续时间的呢?
实际上动画执行的时间取决于当前事务的设置,动画类型取决于图层行为
事务
实际上是 Core Animation
用来包含一系列属性动画集合的机制
任何用指定事务
去改变可以做动画的图层属性都不会立刻发生变化
而是当事务
一旦提交的时候开始用一个动画过渡到新值
事务
是通过 CATransaction
类来做管理
这个类的设计有些奇怪
不像你从它的命名预期的那样去管理一个简单的事务
而是管理了一叠你不能访问的事务
CATransaction
没有属性或者实例方法
并且也不能用 +alloc
和 -init
方法创建它
但是可以用 +begin
和 +commit
分别来入栈或者出栈。
任何可以做动画的图层属性都会被添加到栈顶的事务
你可以通过 +setAnimationDuration:
方法设置当前事务的动画时间
或者通过 +animationDuration 方法来获取值(默认0.25秒)。
Core Animation
在每个 run loop
周期中自动开始一次新的事务
即使你不显式的用 [CATransaction begin]
开始一次事务
任何在一次 run loop
循环中属性的改变都会被集中起来
然后做一次0.25秒的动画
修改隐式动画
于是我么可以尝试使用Core Animation
来修改动画的属性
我们可以用当前事务的+setAnimationDuration:
方法来修改动画时间
但在这里我们首先起一个新的事务
于是修改时间就不会有别的副作用
因为修改当前事务的时间可能会导致同一时刻别的动画(如屏幕旋转)
所以最好还是在调整动画之前压入一个新的事务
- (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.testLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//commit the transaction
[CATransaction commit];
}
UIView 有两个方法
+beginAnimations:context:
和+commitAnimations
这与
CATransaction
的+begin
和+commit
方法类似
实际上在+beginAnimations:context:
和+commitAnimations
之间所有视图或者图层属性的改变而做的动画都是由于设置了CATransaction
的原因
在iOS4中,苹果对 UIView 添加了一种基于 block 的动画方法:+animateWithDuration:animations:
这样写对做一堆的属性动画在语法上会更加简单
但实质上它们都是在做同样的事情
CATransaction
的+begin
和+commit
方法在+animateWithDuration:animations:
内部自动调用
这样block中所有属性的改变都会被事务所包含
这样也可以避免开发者由于对+begin
和+commit
匹配的失误造成的风险
完成块
基于 UIView 的 block 的动画允许你在动画结束的时候提供一个完成的动作
CATranscation 接口提供的 +setCompletionBlock: 方法也有同样的功能
- (IBAction)changeColor
{
//begin a new transaction
[CATransaction begin];
//set the animation duration to 1 second
[CATransaction setAnimationDuration:1.0];
//add the spin animation on completion
[CATransaction setCompletionBlock:^{
//rotate the layer 90 degrees
CGAffineTransform transform = self.colorLayer.affineTransform;
transform = CGAffineTransformRotate(transform, M_PI_2);
self. testLayer.affineTransform = transform;
}];
//randomize the layer background color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self. testLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//commit the transaction
[CATransaction commit];
}
注意旋转动画要比颜色渐变快得多
这是因为完成块是在颜色渐变的事务提交并出栈之后才被执行
于是,用默认的事务做变换,默认的时间也就变成了0.25秒
另一篇讨论 View 的根 layer 为什么没有动画