本文首发地址html
翻译说明
transaction - 事务
复制代码
经过 Core Animation 提供的基础,咱们能够轻松的为 app 的图层建立复杂的动画,并把动画扩展到拥有图层的视图上。可动画的例子包含:改变图层的尺寸、改变图层在屏幕上的位置、应用旋转形变或者改变它的透明度。经过 Core Animation,初始化一个动画一般和改变属性的值同样简单,可是你也能够建立一个动画,而后显式的设置动画参数。git
关于建立高级动画的更多信息,能够参见高级动画技巧。github
你能够根据你的需求执行简单的隐式动画或者显式动画。隐式动画使用默认时间和动画属性去执行动画,而显式动画须要你去配置你使用的动画对象的参数。因此隐式动画更适用于你想执行一个没有大量代码的动画改变,并且默认的时间对你是适用的。算法
简单动画包含改变图层的属性以及让 Core Animation 随着时间使这些改变更画执行。图层定义了不少影响视觉外观的属性。改变这些属性是一种动画改变图层外观的方式。例如,将图层的透明度从 1.0 改成 0.0 会形成图层淡出透明。数组
重要:虽然有时你能够直接使用 Core Animation 的接口去执行图层支持视图的动画,但这一般须要额外的步骤。关于如何共同使用 Core Animation 和图层支持视图的更多信息,请参见如何动画图层支持视图。bash
你只须要更新图层对象的属性便可触发隐式动画。当在图层树上修改图层对象时,这些对象会当即反应你的改变。然而,图层对象的视觉外观不会当即改变。Core Animation 会将你的修改当作一个触发器,去建立和安排一个或者多个隐式动画以供执行。所以,像 例3-1 的改变会形成 Core Animation 为你建立一个动画对象,而后在下次更新循环中开始运行这个动画。app
例 3-1 隐式改变更画ide
theLayer.opacity = 0.0;
复制代码
使用一个动画对象来制做显式改变,建立一个 CABasicAnimation 对象,用该对象配置动画参数。在将动画添加到图层以前,你能够设置动画的起止值,改变持续时间,或者改变更画的其余参数。例3-2 展现了如何经过使用动画对象淡出图层。当建立对象时,你指定你想动画的属性的关键路径,而后设置动画参数。为了执行该动画,你可使用 addAnimation:forKey:
方法将它添加到你想执行动画的图层上。oop
例 3-2 显式改变更画布局
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
[theLayer addAnimation:fadeAnim forKey:@"opacity"];
// 将图层的实际数据改成结束值
theLayer.opacity = 0.0;
复制代码
提示:当建立显式动画时,推荐你给动画对象的 fromValue
属性赋值。若是你没给该属性赋值,Core Animation 会使用图层的当前值做为起始值。若是你已经更新了该属性的结束值,可能不会出现你想要的结果。
不像隐式动画,它会更新图层对象的数据值,显式动画不会修改图层树的数据。显式动画仅执行动画。在动画结束时,Core Animation 会从图层移除动画对象,而后使用图层的当前数据值重绘图层。若是你想显式动画的改变是永久的,你必须像前面的例子同样更新图层属性。
隐式动画和显式动画一般在当前 run loop 循环结束后开始执行,而且当前线程必须有 run loop 才能执行动画。若是修改多个属性,或者你对图层添加多个动画对象,全部的属性改变更画会同时执行。例如,你能够在同一时间配置两个动画来实现淡出图层的同时将它移动到屏幕外。然而,你也能够配置动画对象在特定的时间开始。关于修改动画时间的更多信息,可参见自定义动画时间。
基于属性的动画将属性从起始值改变为终止值,CAKeyframeAnimation 对象容许你经过一组目标值来实现动画,它能够是线性的,也能够不是。一个关键帧是由一组目标值和目标值什么时候执行的时间组成。在最简单的配置中,你可使用数组指定值和时间。例如改变图层的位置,你能够经过下面的路径改变。该动画对象使用你指定的关键帧,经过在给定时间段内从一个值插值到下一个值来构建动画。
图 3-1 展现了一个图层位置属性的 5秒动画。该位置动画跟随一个由 CGPathRef 数据类型指定的路径。该动画的代码如例3-3所示。
例 3-3 展现了实现图3-1 动画的代码。本例中的 path 对象用于定义动画每帧的图层位置。
例 3-3 建立弹跳关键帧动画
// 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"];
复制代码
关键帧动画最重要的一部分就是关键帧的值。这些值定义了动画执行过程当中的行为。指定关键帧的主要方式是当作对象的数组,值包含 CGPoint
的数据类型(如图层的 anchorPoint
和 position
属性),你也能够指定一个 CGPathRef 数据类型代替。
当指定数组值时,放在数组中元素的数据类型依赖于属性。你能够直接添加一些对象到数组中,可是,这些对象在添加前必须转换为id,全部标量类型或结构都必须由对象包装,例如:
CGRrect
类型的属性(如 bounds
和 frame
属性),将其包成 NSValue 对象。CATransform3D
矩阵包成 NSValue 对象。动画该属性会形成关键帧动画将每个形变矩阵应用到图层上。borderColor
属性,在添加到数组以前,将每一个 CGColorRef
数据类型转为 id 类型。contents
属性时,指定一个 CGImageRef
数据类型的数组。对于 CGPoint 类型的属性,你能够建立一个 points(包成 NSValue 的对象)的数组,或者你可使用 CGPathRef 对象去指定一个路径去跟随。当你指定 points 数组时,关键帧动画对象在每一个连续点之间画一条直线,并沿着该路径执行。当你制定一个 CGPathRef 对象时,动画在路径的起始点开始动画,跟随它的轮廓,包括任何曲线。你可使用开放或者封闭路径。
关键帧动画的时间和节奏比基础动画更加复杂,这有几个你可使用控制的属性:
calculationMode
属性定义了计算动画时间的算法。该属性的值影响其余时间相关的属性如何被使用。
calculationMode
属性值设置为 kCAAnimationLinear
或 kCAAnimationCubic
,它们使用提供的时间信息去生成动画。这些模式给你动画时间最大的控制。calculationMode
属性值设置为 kCAAnimationPaced
或 kCAAnimationCubicPaced
,它不依赖由 keyTimes
或 timingFunctions
属性提供的外部时间值。时间值是被隐式计算用来提供动画的恒定速度。calculationMode
属性值设置为 kCAAnimationDiscrete
,使动画属性在没有任何插值的状况下从一个关键帧值跳到下一个关键帧值。此计算模式使用 keyTimes
属性中的值,但忽略 timingFunctions
属性。keyTimes
属性指定应用每一个关键帧值的时间标记。该属性仅在计算模式设置为 kCAAnimationLinear
、 kCAAnimationDiscrete
、 kCAAnimationDiscrete
的时候被使用。它不会被用于节奏动画。
timingFunctions
属性指定每一个关键帧片断的时间曲线,该属性会覆盖继承的 timingFunction
属性。
若是你想本身处理动画时间,使用 kCAAnimationLinear
或 kCAAnimationCubic
模式和 keyTimes
timingFunctions
属性。keyTimes
属性定义了应用每一个关键帧的时间点。全部中间值的定时由定时功能控制,容许你将 ease-in 或 ease-out 曲线应用于每一个线段。若是不指定任何定时功能,则定时是线性的。
动画一般会执行完成,但若是须要,你也可使用下面的方法提早中止动画:
removeAnimationForKey:
方法去移除动画对象。该方法使用 addAnimation:forKey:
方法传递的 key 来识别动画。你指定的 key 不能为 nil。removeAllAnimations
方法。该方法会当即移除全部动画,并使用当前的状态信息重绘图层。注意:你不能直接移除图层的隐式动画。
当你移除图层上的动画对象时,Core Animation 会使用图层的当前值来重绘图层。由于当前值一般是动画的结束值,这会形成图层的外观忽然闪一下。若是你想图层的外观仍然保持在动画的最后一帧上,你可使用表现树里的对象来获取最后的值,而后设置在图层树的对象上。
关于临时暂停动画的更多信息,请参见暂停和重启图层动画。
若是你想同时对图层应用多个动画,你可使用 CAAnimationGroup 对象将它们打包在一块儿。使用组对象能够经过提供单独配置点来简化多个动画对象的管理。应用于组的时间和持续时间值会覆盖单个动画对象中的相同值。
例 3-4 展现了你使用组动画来用一样的持续时间同时执行两个线相关(border-related)的动画。
例 3-4 同时执行两个动画
// Animation 1
CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
NSArray* widthValues = [NSArray arrayWithObjects:@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 = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
(id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor, nil];
colorAnim.values = colorValues;
colorAnim.calculationMode = kCAAnimationPaced;
// Animation group
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration = 5.0;
[myLayer addAnimation:group forKey:@"BorderChanges"];
复制代码
一种更高级的同时执行多个动画的方法是使用事务对象。事务 经过容许你建立内嵌动画集合和给每一个动画参数赋不一样的值来提供更多的灵活性。关于如何使用事务对象,请参见显式事务容许你改变更画参数。
Core Animation 支持检测动画的开始或结束。这些通知是执行任何与动画相关的内务处理任务的好时机。例如,你可能使用开始的通知去设置一些相关状态信息,使用响应结束通知取消该状态。
通知动画状态有两种不一样的方式:
setCompletionBlock:
方法在当前事务中添加一个完成块(completion block)。当事务中全部的动画结束时,事务会执行你的完成块。animationDidStart:
和 animationDidStop:finished:
代理方法。若是你想将两个动画串在一块儿,以实现当一个动画结束时,另外一个动画开始的需求。不要使用动画通知。使用动画对象的 beginTime
属性在所需的时间去开始每个动画对象。为了将两个动画串在一块儿,你能够第一个动画的结束时间设置为第二个动画的开始时间。关于动画时间值的更多信息,请参见自定义动画时间。
由于 iOS 的视图底层都有图层,因此视图类直接从图层对象中获取大部分数据。所以,你在图层上的改变也会自动反映到视图对象上。该行为意味着你可使用 Core Animation 或者视图接口 的任意一种来作你的改变。
若是你想使用 Core Animation 类去初始化动画,你必须从基于视图的动画块内部发出全部 Core Animation 调用。视图类默认禁止图层动画,但在动画块中能够从新启用。因此你在动画块外作的全部改变都是无动画的。例 3-5 展现了如何隐式改变透明度和显式的改变位置的例子。在该例中,myNewPosition 变量是在以前被计算好的,在块中被获取。两个动画在同一时间开始,可是透明度动画在默认的时间执行,而位置动画在动画对象指定的时间里执行。
例 3-5 动画 iOS 的视图
[UIView animateWithDuration:1.0 animations:^{
// 隐式改变 opacity
myView.layer.opacity = 0.0;
// 显式改变 position
CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:@"position"];
theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position];
theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition];
theAnim.duration = 3.0;
[myView.layer addAnimation:theAnim forKey:@"AnimateFrame"];
}];
复制代码
动画 OS X 上的图层支持视图,最好使用视图自己的接口。你应该极少或者毫不直接修改你的图层支持 NSView 对象的图层。AppKit 会建立配置图层对象,当你 app 运行时管理它们。修改图层可能会形成视图和图层之间的不一样步,这会形成意想不到的结果。对于图层支持视图,你的代码必定不要修改图层对象的如下属性:
重要:上述限制不适用于图层托管视图。若是你建立了图层对象并将其与视图手动关联,则你有责任修改该图层的属性并保持相应的视图对象同步。
AppKit 的图层支持视图默认禁止隐式动画。图层的动画代理对象自动为你生效隐式动画。若是你想直接动画图层的属性,你能够经过编码将当前 NSAnimationContext 对象的 allowsImplicitAnimation 值改成 YES。再一次说明,你只能对除上述列表之外的属性进行动画。
若是你使用基于约束的布局规则去管理你的视图的位置,记得更新视图约束是你动画的一部分。在配置动画时,必须删除可能干扰动画的任何约束,约束影响对视图的位置或大小所作的任何更改。若是你正在对其中任何一个项进行动画化更改,你能够删除约束,进行更改,而后应用任何须要的新约束。
有关约束以及如何使用它们来管理视图布局的更多信息,请参见Auto Layout 指南。