- 属性动画
CAAnimationDelegate在任何头文件中都找不到,但是可以在CAAnimation头文件或者苹果开发者文档中找到相关函数。
当更新属性的时候,我们需要设置一个新的事务,并且禁用图层行为。否则动画会发生两侧,一个是因为显式的CABasicAnimation,另一次是因为隐式动画(关于隐式动画后面我会介绍)。
例如我们更改主视图的背景颜色:
CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"backgroundColor"; animation.toValue = (__bridge id)color.CGColor; animation.delegate = self; //apply animation to layer [self.view.layer addAnimation:animation forKey:nil];
// 显式动画结束时调用。- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag { //set the backgroundColor property to match animation toValue [CATransaction begin]; [CATransaction setDisableActions:YES]; self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue; [CATransaction commit]; }
使用代理时回调动画结束函数,要判断是哪个图层调用的。
但当真正跑在iOS设备上时,我们发现在
注意:动画本身会作为一个参数传入委托的方法,也许你会认为可以控制器中把动画存储为一个属性,
然后回调比较,但是实际上并不起作用,因为委托传入的动画参数是原始值的深拷贝,
从而不是同一个值。-animationDidStop:finished:
委托方法调用之前,
指针会迅速返回到原始值,问题在于回调方法在动画完成之前已经被调用了,但不能保证这发生在属性动画
返回初始状态之前。这同时也很好地说明了为什么要在真实的设备上测试动画代码,而不仅仅是模拟器。
- 关键帧动画
CABasicAnimation揭示了大多数隐式动画背后依赖的机制,这的确很有趣,但是显式地给图层添加CABasicAnimation
相较于隐式动画而言,只能说费力不讨好。
CAKeyframeAnimation
是另一种UIKit没有暴露出来但功能强大的类。和CABasicAnimation
类似,CAKeyframeAnimation
同样是CAPropertyAnimation
的一个子类,它依然作用于单一的一个属性,但是和CABasicAnimation
不一样的是,它不限制于设置一个起始和结束的值,而是可以根据一连串随意的值来做动画。
关键帧起源于传动动画,意思是指主导的动画在显著改变发生时重绘当前帧(也就是关键帧),每帧之间剩下的绘制(可以通过关键帧推算出)将由熟练的艺术家来完成。CAKeyframeAnimation
也是同样的道理:你提供了显著的帧,然后Core Animation在每帧之间进行插入。
注意到序列中开始和结束的颜色都是蓝色,这是因为CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"backgroundColor"; animation.duration = 2.0; animation.values = @[ (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor greenColor].CGColor, (__bridge id)[UIColor blueColor].CGColor ]; [self.colorLayer addAnimation:animation forKey:nil];
CAKeyframeAnimation
并不能自动把当前值作为第一帧(就像CABasicAnimation
那样把fromValue
设为nil
)。动画会在开始的时候突然跳转到第一帧的值,然后在动画结束的时候突然恢复到原始的值。所以为了动画的平滑特性,我们需要开始和结束的关键帧来匹配当前属性的值。还可以使用贝塞尔曲线绘制曲线路径,然后沿着路径运动。通过
rotationMode
自动对齐图层到曲线- 虚拟属性
之前提到过属性动画实际上是针对于关键路径而不是一个键,这就意味着可以对子属性甚至是虚拟属性做动画。但是虚拟属性到底是什么呢?
考虑一个旋转的动画:如果想要对一个物体做旋转的动画,那就需要作用于transform
属性,因为CALayer
没有显式提供角度或者方向之类的属性
toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI, 0, 0, 1)];
使用NAValue指定结构体类型值,byValue是叠加变换。一般我们使用
transform.rotation关键路径应用动画,而不是
用
transform本身。
transform.rotation
而不是transform
做动画的好处如下:
- 我们可以不通过关键帧一步旋转多于180度的动画。
- 可以用相对值而不是绝对值旋转(设置
byValue
而不是toValue
)。- 可以不用创建
CATransform3D
,而是使用一个简单的数值来指定角度。- 不会和
transform.position
或者transform.scale
冲突(同样是使用关键路径来做独立的动画属性)。
transform.rotation
属性有一个奇怪的问题是它其实并不存在。这是因为CATransform3D
并不是一个对象,它实际上是一个结构体,也没有符合KVC相关属性,transform.rotation
实际上是一个CALayer
用于处理动画变换的虚拟属性。你不可以直接设置
transform.rotation
或者transform.scale
,他们不能被直接使用。当你对他们做动画时,Core Animation自动地根据通过CAValueFunction
来计算的值来更新transform
属性。
CAValueFunction
用于把我们赋给虚拟的transform.rotation
简单浮点值转换成真正的用于摆放图层的CATransform3D
矩阵值。你可以通过设置CAPropertyAnimation
的valueFunction
属性来改变,于是你设置的函数将会覆盖默认的函数。