Do What I mean, not what I say
Implicit Animations
首先看一个demo
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView; @property (nonatomic, weak) IBOutlet CALayer *colorLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//create sublayer
self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[self.layerView.layer addSublayer:self.colorLayer]; }
- (IBAction)changeColor {
//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]
}
@end
在这个程序中,我们并没有看到任何代码和动画相关。我们只是单纯的更改了一个非UIView关联的Layer的属性(一定要是非UIView相关联的Layer,后文会解释原因)。当我们改变一个属性时,Core Animation是怎么判断动画类型和持续时间呢?实际上动画执行的时间取决于当前事务(Transaction)的设置,动画类型取决于图层行为(Layer Action)。事务实际上是Core Animation用来包含一系列动画集合的机制,任何时候在一个指定事务中去改变都不会立刻发生变化,而是当事务提交的时候开始用一个动画过渡到新值。事务是通过CATransation 类来管理的,这个类的设计很有意思,不像它的名字,它管理了一个你不能直接访问的事务的栈。CATransaction没有属性或者实例方法,并且也不能使用alloc,init来创建。然而,你可以通过类方法,+begin和+commit来把一个新的事务压入栈中,或者推出当前事件。
任何的一个可动画的图层的属性的变化都会被添加到栈顶的事务。(Any layer property change that can be animated will be added to the topmost transaction in the stack.)你可以通过+setAnimationDuration: 方法来设置当前事务的动画时间,或者,通过+animationDuration 方法来获取当前的动画时间。
Core Animation 在每一个run loop周期中自动开始一个新的事务,即是你不显示的使用[CATransaction begin]开始一个新的事务,任何一次在run loop循环中属性的改变都会被集中起来,然后坐一次0.25秒的动画。明白了这些之后,我们就可以轻松的修改变色动画的时间了,我们当然可以使用当前事务的+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.colorLayer.backgroundColor = [UIColor colorWithRed:red
//commit the transaction
[CATransaction commit];
}
完成块
基于UIView的block动画允许你在动画结束的时候提供一个完成块。CATransaction接口提供的+setCompleteBlock:方法也有同样的功能。
- (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.colorLayer.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.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//commit the transaction
[CATransaction commit];
}
注意,旋转动画要比颜色渐变快得多,这事因为完成块是在颜色渐变得事务提交并出栈之后才被执行,所以,用默认的事务做变换,默认的时间也变成了0.25秒。