CAKeyframeAnimation
关键帧动画,也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation。
属性解析:
publicvar
values: [AnyObject]?
values: 是关键帧动画最重要的一部分,该值定义了动画执行的行为,它是一个数组,里面的元素理解为”关键帧"(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一关键帧。在添加到数组之前,需要注意:有些对象可以直接添加到数组当中,但是有些对象必须在被添加之前需要进行包装,比如:有些对象需要包装为id类型,而标量数据类型或者结构体必须被包装成为对象,例如:
1:如果属性是 CGRect类型(比如:
bounds and frame 属性),应该包装每一个矩形成为NSValue对象。
2:对于layer的transform属性,需要包装每一个值CATransform3D成为NSValue对象。
3:对于borderColor属性,在被添加到数组之前,需要把CGColorRef数据类型包装成为id类型。
4:对于CGFloat值,包装为NSNumber对象。
5:对于layer的contents属性,使用CGImageRef属性类型。
6:对于CGPoint数据类型,可以使用NSValue对象进行包装,也可以使用CGPathRef对象使用路径进行包装。
publicvar
path: CGPath?
这是一个可选的路径对象,默认是nil.它定义了动画的行为,当path的值非nil时,将覆盖values属性的值,作用与values属性一样(即:如果你设置了path,那么values将被忽略。)对于常速路径动画,
calculationMode应该被设置为 paced。我们可以设置一个CGPathRef\CGMutablePathRef,让层按照这个路径进行动画移动。
publicvar
keyTimes: [NSNumber]?
这是一个可选的轨迹动画的时间数组,数组中的每一个值都是NSNumber对象,并且取值范围在
[0,1]。它定义了动画的步调,数组中的每一个值都与
values中的值一一对应(可以理解为对应的关键帧指定对应的时间点,keyTimes中的每一个时间值都对应values中的每一帧.)。
当keyTimes没有设置的时候,各个关键帧的时间是平分的。默认情况下,一帧动画的播放,分割的时间是动画的总时间除以帧数减去一。你可以通过下面的公式决定每帧动画的时间:总时间/(总帧数-1)。例如,如果你指定了一个5帧,10秒的动画,那么每帧的时间就是2.5秒钟:10/(5-1)=2.5。你可以做更多的控制通过使用
keyTimes 关键字,你可以给每帧动画指定总时间之内的某个时间点。
例子: @[@(0),@(0.2),@(0.5),@(1)];这里面设置的三个的动画时间,假设总时间是10秒,第一段的时间为2秒(0.2
- 0)*10第二段的时间为3秒(0.5-0.2)*10第三段的时间为5秒(1-0.5)*10。
publicvar
timingFunctions: [CAMediaTimingFunction]?
这是一个可选数组,数组中的值是CAMediaTimingFunction类型,如果
values数组定义了n关键帧,那么该数组就需要 n-1个CAMediaTimingFunction值。每一个CAMediaTimingFunction值描述了关键帧从一个值到另一个值之间过渡的步调(即:运动的时间函数)。
kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。
kCAMediaTimingFunctionDefault (默认时间函数)
各值对应图形如下:
publicvar
calculationMode: String 计算动画的时间
该属性是关键帧动画中还有一个非常重要的参数,所谓计算模式:其主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint和
position进行的动画。
当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算
calculationMode目前提供如下几种模式:
kCAAnimationLinear默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算,该模式提供了最大化控制动画的时间
kCAAnimationDiscrete离散的,不进行插值计算,所有关键帧直接逐个进行显示,该模式使用keyTimess属性,但是忽略
timingFunctions属性。
kCAAnimationPaced使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效
kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑
kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的
注意:如果你想自己处理动画的时间,那么可以使用kCAAnimationLinear
or kCAAnimationCubic
模式和keyTimes、timingFunctions属性。keyTimes将定义每一帧所对应的时间,每帧时间的中间值由timingFunctions设置对应的值进行控制。如果没有设置,将使用默认值。
如果动画使用了kCAAnimationCubic计算模式,那么下面这些属性将控制插值方案。每一帧都有可能与拉力,连续性,偏斜等值相关,这些值得范围是[-1, 1] (this defines a Kochanek-Bartels
spline, see http://en.wikipedia.org/wiki/Kochanek-Bartels_spline).
public var
tensionValues: [NSNumber]? 该值控制着曲线的紧密度(正值将越紧,负值将越宽松)
publicvar
continuityValues: [NSNumber]? 该值控制片段之间的链接(正值将有锋利的圆角,负值将是倒立的圆角)
publicvar
biasValues: [NSNumber]? 该值定义了曲线发生的地点(正值将在在控制点前移动曲线,负值将在控制点后移动)
上面每一个数组,第一个值将定义第一个控制点正切的行为,第二个值将是第二个切点。如果没有具体的值,默认为0.
publicvar
rotationMode: String? 定义是否沿着路径旋转匹配对象动画路径切线,值可能为kCAAnimationRotateAuto和kCAAnimationRotateAutoReverse.默认是nil.如果没有路径对象,设置该属性值将无效,kCAAnimationRotateAutoReverse为了匹配正切将添加180°.
下面是官方文档的部分翻译内容:
基本的动画对象改变属性是从一个开始值到结束值。而一个CAKeyframeAnimation对象能够让你设置一系列值来进行动画并且可以设置对应值所进行的动画时间,通过简单的设置关键帧和关键帧时间两个数组即可。为了改变layer的位置,我们也可以使用路径进场动画。
图 3-显示了5秒的layer’sposition
属性动画效果,位置的动画使用了路径,路径是一个具体的CGPathRef数据类型
。动画部分的代码为Listing
3-3.
Figure 3-1 5-second
keyframe animation of a layer’s position property
Listing 3-3 Creating
a bounce keyframe animation
// create a CGPath that implements two arcs (a bounce) CGMutablePathRef thePath = CGPathCreateMutable(); CGPathMoveToPoint(thePath,NULL,74.0,74.0); CGPathAddCurveToPoint(thePath,NULL,74.0,500.0, 320.0,500.0, 320.0,74.0); CGPathAddCurveToPoint(thePath,NULL,320.0,500.0, 566.0,500.0, 566.0,74.0); CAKeyframeAnimation * theAnimation; // Create the animation object, specifying the position property as the key path. theAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"]; theAnimation.path=thePath; theAnimation.duration=5.0; // Add the animation to the layer. [theLayer addAnimation:theAnimation forKey:@"position"];
停止正在进行的动画
动画一般都是正常进行直到他们完成,但是我们也能够提前停止动画,如果需要提前停止动画,我们可以使用下列方法之一:
1)去除layer上单一的动画对象,调用layer的removeAnimationForKey:
方法去除动画对象,该方法对应layer的addAnimation:forKey:方法添加动画对象,key值不能够为nil,并且相同。
2)去除layer上所有的动画对象,通过调用layer的removeAllAnimations方法,该方法将立马去除所有当前执行的动画并且重绘当前layer的状态信息。
注意:不能够直接去除隐式动画。
当去除layer上的动画对象时,Core
Animation将使用layer当前的values进行重绘,因为当前值是动画的最后值,这能够导致layer显示突然的跳动,如果想要layer的显示保持最后一帧动画,你能够使用对象的图层树来获取最终的值,并设置layer树对象。对于更多暂时停止动画,可以看Listing5-4.
多动画部分同时进行
如果想要对layer对象同事使用多个动画,我们能够使用CAAnimationGroup对象,使用群组动画对象能够非常方便的管理多个动画对象。设置Timing
and duration属性值将取代当个动画对应的属性值。Listing 3-4将告诉我们如何使用组合与border-relate的动画。
Listing 3-4 Animating
two animations together
// Animation 1 CAKeyframeAnimation* widthAnim = [CAKeyframeAnimationanimationWithKeyPath:@"borderWidth"]; NSArray* widthValues = [NSArrayarrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil]; widthAnim.values = widthValues; widthAnim.calculationMode = kCAAnimationPaced; // Animation 2 CAKeyframeAnimation* colorAnim = [CAKeyframeAnimationanimationWithKeyPath:@"borderColor"]; NSArray* colorValues = [NSArrayarrayWithObjects:(id)[UIColorgreenColor].CGColor, (id)[UIColorredColor].CGColor, (id)[UIColorblueColor].CGColor, nil]; colorAnim.values = colorValues; colorAnim.calculationMode = kCAAnimationPaced; // Animation group CAAnimationGroup* group = [CAAnimationGroupanimation]; group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil]; group.duration = 5.0; [myLayer addAnimation:group forKey:@"BorderChanges"];
更高级的方式对于组合动画可以使用事务对象,事务将通过更多便利的方式来创建嵌套动画并且可以为每一个赋值不同的动画参数。对于更多详情的事务信息,可以看 Explicit
Transactions Let You Change Animation Parameters.
检测动画
Core Animation支持检测动画的开始和结束,这些通知是非常有利于处理与动画相关的任务。例如:你可能使用开始的通知设置一些相关的开始信息而且使用结束通知来信息处理这些状态信息。
这里有两种不同的方式通知有关于动画的状态:
1)添加block到当前事务(transaction)当中使用setCompletionBlock:方法,当所有的动画在事务中完成,transaction将执行你的block。
2)设置代理对象,实现代理方法animationDidStart:
and animationDidStop:finished:。
如果你想束缚两个动画,实现单一个动画完成又立马开始另外一个动画,并且不想使用动画通知操作。那么可以使用beginTime属性,这是每一个动画开始的时间,为了实现一个动画完成又立马开始另外一个动画,可以设置第二个动画的开始时间是第一个动画的结束时间。对于更多的信息可以看Customizing
the Timing of an Animation.