接上文。本文主要包括 如何构建一个layer 的层次结构 / 高级动画的操作 / 如何改变layer的默认行为 / 如何提升动画性能

构建一个Layer的层次结构
大部分时间,最好的在app中使用layer的方式是和view object一起使用。可是,也有需要通过添加额外的layer object到现有view 层级(为了增强视图层级)的时候。在这样做时,你可能会使用layers,这样可以提供更好的性能。或者,让你能实现一个单独使用视图而难以实现的特性。在这种情况下,您需要知道如何管理 创建的layer层次结构。
重要: OS X 10.8以上版本,推荐最小化使用layer的层级机构,只使用 layer-backed viewslayer 重绘机制允许您自定义layer-backed viewlayer的行为,并且仍然能比使用单独的layer提升性能。
Layer 加入到一个 Layer Hierarchy / Arranging Layers into a Layer Hierarchy
Layer hierarchies 和 view hierarchy非常相似。通过嵌入一个 layer 到另一个,让layer有 类似 parent-child的关系。被嵌入的layer,即sublayer,parent layer,即superlayer。这个关系影响sublayer的许多方面。比如:sublayer的内容 在parent上(above),他的位置和parent的坐标系关联,会收到parent的transform影响。

添加,插入,删除 sublayers

Behavior Methods Description
Adding layers addSublayer: 添加一个新的sublayer object
Inserting layers insertSublayer:above:

insertSublayer:atIndex:

insertSublayer:below:

插入 sublayer
Removing layers removeFromSuperlayer 删除sublayer
Exchanging layers replaceSublayer:with: 用一个代替另一个(先删除一个)
在使用自己创建的图层对象时,可以使用上述方法。你不会使用这些方法来排列属于layer-backed 视图的图层。但是,layer-backed 的视图可以作为你创建的独立layer的父层。

调整 sublayer 的位置和大小

添加和插入子图层时,必须在子图层出现在屏幕上之前设置子图层的大小和位置。 您可以在将子图层添加到图层层次结构后修改其大小和位置,但应该习惯于在创建图层时设置这些值。
您可以使用bounds属性设置子图层的大小,并使用position属性在其superlayer中设置其位置。 边界矩形的原点几乎总是(0,0),并且大小与点中指定的图层的大小无关。 位置属性中的值相对于图层的锚点进行解释,该锚点默认位于图层的中心。 如果不为这些属性赋值,Core Animation会将图层的初始宽度和高度设置为0,并将位置设置为(0,0)。
myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);
重要: layer的长款属性永远都是整形

Layer hierarchies如何影响动画

某些superlayer的属性可能会影响应用于其子图层的动画行为。比如速度speed属性,它是动画速度的乘积。该属性的值默认设置为1.0,但将其更改为2.0会使动画以原始速度的两倍运行。该属性不仅影响其设置的图层,还影响该图层的子图层。这种变化也是可乘的。如果子图层和其superlayer的速度均为2.0,则子图层上的动画将以其原始速度的四倍运行。
大多数其他层的更改影响包含的子层的方式是可预测的。例如,将旋转变换应用到图层会旋转该图层及其所有子图层。同样,更改图层的不透明度会更改其子图层的不透明度。对图层大小的更改遵循调整图层层次结构的布局中所述的布局规则。
调整 你的 Layer hierarchies的布局
Core Animation 支持调整sublayer的位置和size的几个选项,来响应sublayer的变化。iOS中,普遍使用 layer-backed views,这让创建 Layer hierarchies不是那么重要;只有手动布局更新被支持。对OS X来说,几个可用的其他选项让管理Layer hierarchies变得简单。
Layer级别的布局仅仅和你正在使用 单独 layer object 构建的 Layer hierarchies 有关。如果你的 app layer和view关联,使用 view-base的布局来更新视图。

在OS X中,用 约束管理 Layer hierarchies

约束使您可以使用layer及其superlayer或同层图层之间的一组详细关系指定图层的位置和大小。定义约束需要以下步骤:
  • 创建一个或多个CAConstraint对象。使用这些对象来定义约束参数。
  • 将您的约束对象添加到其修改属性的图层。
  • 检索共享的CAConstraintLayoutManager对象并分配给直接super  layer。
下图显示了可用于定义约束的属性以及它们影响的图层的方面。您可以使用约束根据相对于其他图层的中点边缘位置来更改图层的位置。您也可以使用它们来更改图层的大小。您所做的更改可以与superlayer或与其他图层相关的比例成比例。您甚至可以为所产生的更改添加比例因子或常数。这种额外的灵活性可以使用一套简单的规则非常精确地控制图层的大小和位置。
每个约束对象封装沿同一轴的两个层之间的几何关系。 每个轴最多可以分配两个约束对象,而这两个约束可以决定哪个属性可以更改。 例如,如果您为图层的左侧和右侧边缘指定约束,则图层的大小会发生变化。 如果您为图层的左边缘和宽度指定约束,则图层右边缘的位置会更改。 如果您为其中一个图层的边指定单个约束,则Core Animation会创建一个隐式约束,以便将该图层的大小保持在给定维中。
创建约束时,您必须始终指定三条信息:
  • 您想要约束的图层的方面
  • 要用作参考的图层
  • 比较中使用的参考图层的方面
下图显示了一个简单的约束,将一个图层的垂直中点固定到其上层的垂直中点。 引用superlayer时,请使用字符串superlayer。 这个字符串是保留用于引用superlayer的特殊名称。 使用它可以避免需要指向图层的指针或知道图层的名称。 它还允许您更改superlayer并将约束自动应用于新父级。 (在创建与兄弟图层相关的约束时,必须使用其名称属性标识兄弟图层。
[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
relativeTo:@”superlayer”
attribute:kCAConstraintMidY]];
要在运行时应用约束,您必须将共享的CAConstraintLayoutManager对象附加到需要的superlayer。每层负责管理其子层的布局。将布局管理器分配给父级会告诉Core Animation应用其子级定义的约束。布局管理器对象自动应用约束。将它分配给父层后,不必告诉它更新布局。
要查看更具体情况下的约束如何工作,请考虑下图。在这个例子中,设计要求layerA的宽度和高度保持不变,并且layerA保持居中在它的超层中。另外,层B的宽度必须与层A的宽度相同,层B的顶部边缘必须保持在层A的底部边缘以下10个点,并且层B的底部边缘必须保持超过层的底部边缘10个点。下图显示了您将用于为此示例创建子层和约束的代码。
// Create and set a constraint layout manager for the parent layer.
theLayer.layoutManager=[CAConstraintLayoutManager layoutManager];
// Create the first sublayer.
CALayer *layerA = [CALayer layer];
layerA.name = @”layerA”;
layerA.bounds = CGRectMake(0.0,0.0,100.0,25.0);
layerA.borderWidth = 2.0;
// Keep layerA centered by pinning its midpoint to its parent’s midpoint.
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
relativeTo:@”superlayer”
attribute:kCAConstraintMidY]];
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
relativeTo:@”superlayer”
attribute:kCAConstraintMidX]];
[theLayer addSublayer:layerA];
// Create the second sublayer
CALayer *layerB = [CALayer layer];
layerB.name = @”layerB”;
layerB.borderWidth = 2.0;
// Make the width of layerB match the width of layerA.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth
relativeTo:@”layerA”
attribute:kCAConstraintWidth]];
// Make the horizontal midpoint of layerB match that of layerA
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
relativeTo:@”layerA”
attribute:kCAConstraintMidX]];
// Position the top edge of layerB 10 points from the bottom edge of layerA.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
relativeTo:@”layerA”
attribute:kCAConstraintMinY
offset:-10.0]];
// Position the bottom edge of layerB 10 points
//  from the bottom edge of the parent layer.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
relativeTo:@”superlayer”
attribute:kCAConstraintMinY
offset:+10.0]];
[theLayer addSublayer:layerB];
可以对OS Xlayer 层级关系设置Autoresizing Rules
与UIView类似,Autoresizing Rules是调整OS X中图层大小和位置的另一种方法。使用Autoresizing Rules,可以指定图层的边缘是否应保持与超级图层相应边缘的固定或可变距离。您可以同样指定图层的宽度或高度是固定的还是可变的。关系总是在图层和它的超级图层之间。您不能使用Autoresizing Rules来指定兄弟图层之间的关系。
要为图层设置Autoresizing Rules,您必须将适当的常量分配给图层的autoresizingMask属性。默认情况下,图层被配置为具有固定的宽度和高度。在布局过程中,Core Animation会自动为您计算图层的精确大小和位置,并且会涉及基于许多因素的复杂计算集。 Core Animation在请求代理执行任何手动布局更新之前应用自动调整行为,因此您可以根据需要使用委托调整自动调整布局的结果。
在iOS和OS X上,您可以通过在super layer的delegate object上实现layoutSublayersOfLayer:方法来手动处理布局。 您可以使用该方法调整当前嵌入到图层中的任何子图层的大小和位置。 在进行手动布局更新时,您需要执行必要的计算来定位每个子图层。
如果您正在实现自定义layer子类,则子类可以覆盖layoutSublayers方法并使用该方法(而不是delegate)来处理任何布局任务。 如果需要完全控制自定义图层类中子图层的位置,则应该只覆盖此方法。 替换默认实现可以防止Core Animation在OS X上应用约束或自动修复规则。
Sublayer 裁切
与视图不同,super layer不会自动剪裁位于边框矩形外的sublayer的内容。 相反,super layer允许其sublayer默认全部显示。 但是,可以通过将图层的masksToBounds属性设置为YES来重新启用裁剪。
如果指定了层的剪切蒙版,则其形状包含图层的角半径。 下图显示了一个图层,演示了masksToBounds属性如何影响具有圆角的图层。 当属性设置为NO时,即使子层延伸到其父层的边界之外,子层也会全部显示。 将该属性更改为YES会导致其内容被裁剪。
Layer间转换坐标值
有时,您可能需要将一个图层中的坐标值转换为不同图层中同一屏幕位置处的坐标值。 CALayer类提供了一组可以用于此目的的简单转换方法:
  • convertPoint:fromLayer:
  • convertPoint:toLayer:
  • convertRect:fromLayer:
  • convertRect:toLayer:
除了转换 point/rect 之外,还可以使用convertTime:fromLayer:和convertTime:toLayer:方法在图层之间转换时间值。 每个图层都定义了自己的本地时间空间,并使用该时间空间将动画的开始和结束与系统的其余部分同步。 这些时间空间默认同步; 但是,如果更改一组图层的动画速度,那么这些图层的时间空间会相应更改。 您可以使用时间转换方法来解决任何此类因素,并确保两个图层的时间同步。
高级动画技巧
转换动画支持改变layer的显示 / Transition Animations Support Changes to Layer Visibility
Transition Animation object 创建了一个动态的可视化的layer的过度。Transition 对象的典型使用场景是协调的显示或者隐藏一个layer。与 property-based 动画不同,property-based动画是当layer的属性变化时的动画,但是 transition 对象操作的是layer的缓存图片,可以实现property-based动画通过修改值而实现起来比较困难或者不可能实现的效果。标准的transition类型可以让你实现reveal, push, move, or crossfade 动画。OSX上,你可以使用core image的 filter来创建transition,这样就可以实现其他效果,比如:wipes, page curls, ripples或者你的自定义效果。
你需要创建一个CATransition object来实现transition 动画。把它加入layer来启动动画。你同样用这个对象,指定他的transition type来执行这个动画的开始和结束点。您也不需要使用整个transition animation,transition object可让你指定动画时使用的开始和结束进度值。这些值可让您在中点处开始或结束动画。
下图显示了用于在两个视图之间创建push transition的代码。 在该示例中,myView1和myView2位于同一父view中的相同位置,但只有myView1当前可见。 push transition会导致myView1向左滑出并淡入,直到myView2从右侧滑入并变为可见时隐藏。 更新两个视图的隐藏属性可确保在动画结束时两个视图的可见性都是正确的。
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
// Add the transition animation to both layers
[myView1.layer addAnimation:transition forKey:@”transition”];
[myView2.layer addAnimation:transition forKey:@”transition”];
// Finally, change the visibility of the layers.
myView1.hidden = YES;
myView2.hidden = NO;
当两个图层涉及相同的转换时,您可以为两者使用相同的转换对象。 使用相同的转换对象还可以简化必须编写的代码。 但是,您可以使用不同的转换对象,并且如果每个图层的转换参数不同,则肯定会这样做。
下图显示了如何使用Core Image Filter在OS X上实现transition效果。使用所需参数配置filter后,将其分配给rtansition 对象的filter属性。 之后,应用动画的过程与其他类型的动画对象相同。
// Create the Core Image filter, setting several key parameters.
CIFilter* aFilter = [CIFilter filterWithName:@”CIBarsSwipeTransition”];
[aFilter setValue:[NSNumber numberWithFloat:3.14] forKey:@”inputAngle”];
[aFilter setValue:[NSNumber numberWithFloat:30.0] forKey:@”inputWidth”];
[aFilter setValue:[NSNumber numberWithFloat:10.0] forKey:@”inputBarOffset”];
// Create the transition object
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.filter = aFilter;
transition.duration = 1.0;
[self.imageView2 setHidden:NO];
[self.imageView.layer addAnimation:transition forKey:@”transition”];
[self.imageView2.layer addAnimation:transition forKey:@”transition”];
[self.imageView setHidden:YES];
注意:在动画中使用Core Animation Filter时,最棘手的部分是配置filter 例如,bar swipe transition,指定太高或太低的输入角度可能会使其看起来好像没有转换发生。 如果您没有看到您期望的动画,请尝试将您的filter参数调整为不同的值,以查看是否更改了结果。
定制动画的时机
timing 是动画的重要组成。 Core Animation中,你可以通过CAMediaTiming协议的方法和属性为动画指定精确的计时信息。两个Core Animation类采用这个协议。 CAAnimation类,你可以在animation object中指定时间信息。 CALayer类,你可以为隐式动画配置一些与时间相关的功能,但包装这些动画的隐式trasaction object通常会提供优先的默认定时信息。
在考虑timing和animation时,了解layer object如何随时间工作很重要。 每个layer都有自己的local time,用于管理动画时间。 通常,两个不同图层的本地时间足够接近,您可以为每个图层指定相同的时间值,并且用户可能不会注意到任何内容。 但是,图层的本地时间可以由其父图层或其自己的时间参数修改。 例如,更改图层的speed属性会导致该图层(及其子图层)上动画的持续时间按比例变化。
为了帮助您确保给定图层合适的时间值,CALayer类定义了convertTime:fromLayer:和convertTime:toLayer:方法。 你可以使用这些方法将固定时间值转换为图层的local time,或将时间值从一层转换为另一层。 这些方法考虑了可能影响图层local time的media timing properties,并返回可用于其他图层的值。 下图显示了一个示例,您应该定期使用该示例来获取图层的当前local time。 CACurrentMediaTime函数是一个方便的函数,它返回计算机当前的时钟时间,该方法将其转换为图层的本地时间。
Getting a layer’s current local time:
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
一旦在图层有本地时间值,就可以使用该值更新动画对象或图层的与时间相关的属性。通过这些计时属性,您可以实现一些有趣的动画行为,包括:
  • 使用beginTime属性设置动画的开始时间。通常,动画在下一个更新周期开始。您可以使用beginTime参数将动画开始时间延迟几秒钟。将两个动画链接在一起的方法是将一个动画的开始时间设置为与另一个动画的结束时间相匹配。
  • 如果延迟开始动画,可能还需要将fillMode属性设置为kCAFillModeBackwards。即使layer tree中的layer object包含不同的值,该填充模式也会使图层显示动画的起始值。如果没有这个填充模式,你会在动画开始执行之前看到跳转到最终值。
  • autoreverses属性导致动画在指定的持续时间内执行,然后返回到动画的起始值。您可以将此属性与repeatCount属性结合在起始值和结束值之间来回移动。将重复次数设置为整 数(例如1.0)以进行自动对换动画会导致动画停止其初始值。添加额外的half step(例如重复计数为1.5)会导致动画在其最终值上停止。
  • 将timeOffset属性与group animation一起使用可以在稍后时间启动一些动画。
暂停和重新开始动画
要暂停动画,可以利用图层采用CAMediaTiming协议并将layer animation的速度设置为0.0。 将速度设置为0将暂停动画,直到您将该值更改回非零值。 下图显示了一个简单的例子,介绍如何在稍后暂停和恢复动画。
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] – pausedTime;
layer.beginTime = timeSincePause;
}
显示事务transactions能让你改变动画的参数parameters
每个你对layer的改变都必须是事务的一部分。CATransaction 类管理负责管理动画的创建和分组和在合适的时间执行动画。大部分情况,你不需要创建自己的事务。Core Animation 自动隐式在你需要添加显示或者隐式动画的地方创建事务。单数,你可以创建显示事务来更精确的管理动画。
使用CATransation类管理事务。要开始(并且隐式创建)一个新事务,调用begin 类方法;结束事务,嗲用 commit 类方法。在这些调用之间,放入事务的变更部分。比如下图,改变layer的两个属性,代码如下:
[CATransaction begin];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];
一个使用事务的主要原因是在一个显示事务内,你可以改变 duration,timingFunction和其他单数。你也可关联 completion block 到整个 事务,这样你的app可以在一组动画结束时收到通知。改变 动画参数要求使用setValue:forKey方法操作事务字典中对应的key。比如,为了修改默认duration为10秒,你需要修改kCATransactionAnimationDuration key,如下图:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
forKey:kCATransactionAnimationDuration];
// Perform the animations
[CATransaction commit];
你可以在需为不同组动画提供不同默认值的情况中使用 嵌套事务。在事务中再次调用 begin 类方法来实现事务的嵌套。每个 begin 方法调用 必须和commit方法一一对应。下图,内部事务改变与外部事务相同的动画参数,但使用了不同的值。
[CATransaction begin]; // Outer transaction
// Change the animation duration to two seconds
[CATransaction setValue:[NSNumber numberWithFloat:2.0f]
forKey:kCATransactionAnimationDuration];
// Move the layer to a new position
theLayer.position = CGPointMake(0.0,0.0);
[CATransaction begin]; // Inner transaction
// Change the animation duration to five seconds
[CATransaction setValue:[NSNumber numberWithFloat:5.0f]
forKey:kCATransactionAnimationDuration];
// Change the zPosition and opacity
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit]; // Inner transaction
[CATransaction commit]; // Outer transaction
Animations添加透视
应用程序可以在三个空间维度上操作图层,但为了简单起见,Core Animation使用平行投影显示图层,这基本上将场景平整为二维平面。这个默认行为导致具有不同zPosition值的相同大小的layer显示为相同的大小,即使它们在z轴上相距很远。你在三维空间下看到的透视场景就不存在了。但是,你能通过修改layer的 transformation marix来修改这种行为来包含透视信息。
当修改场景透视时,你需要修改super layer(包含被显示的layers)的 sublayerTransform 属性的matrix。修改 superlayer 简化了你需要应用于所有 child layer的相同透视信息的代码。也确保透视被应用于那些层叠在不同平面的相关的 sublayers。
下图显示了为parent layer创建简单透视转换的代码。这里,eyePosition变量指定可沿z轴的相对距离来查看layer的view。通常你可以为eyePosition指定一个正值来保持图层的正确方向。 较大的值会导致更平面的场景,而较小的值会导致层之间更加显着的视觉差异。
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/eyePosition;
// Apply the transform to a parent layer.
myParentLayer.sublayerTransform = perspective;
随着 parent layer的配置,你可以通过改变人意 child layers的 zPosition 属性来观察他们size的、基于eye position的相关距离的变化。
改变layer的默认行为
Core Animation使用 action objects实现了他的layer的隐式动画行为。一个 action object 时一个实现 CAAction 协议的对象,定义了一些 执行到layer上的行为。所有CAAnimation objects实现这个协议,而且这些对象通常被分配在一个图层属性改变的时候被执行。
动态属性是action的一种类型,但是你可以用 任何需要的行为来定义 action。你必须定义你自己的action objects、用你app的layer objects关联这些 action objects。
自定义 Action 对象适配 CAAction 协议
为了创建自己的 action object,让你的一个类遵循 CAAction协议,实现 runActionForKey:object:arguments:方法。这个方法中,使用 可用信息来执行 任何你需要执行到layer上的action。你可以使用这个方法来添加 animaition object到一个layer,或者你可以使用它来执行其他任务。
当你定义了一个 action object,你必须决定你的action怎样被触法。action的触发器定义了你注册action的key,action objects 能被任何以下情景触法:
  • layer的某一个属性值改变了。这个改变可以是layer的任意属性值的改变,不仅仅是可以动态的属性。你也可以把自定义的属性和action关联。标识这个action的key是 属性的名称。
  • layer变为可见或者被加入一个layer层级中时。标识这个action的key是 kCAOnOrderIn。
  • layer被从一个层级中移除时。标识这个action的key是 kCAOnOrderOut。
  • layer在一个 变换动画中即将启动时。标识这个action的key是 kCATransition。
Action objects必须被安装到一个 layer上,才能有效果
在 action 能被执行前, layer 需要 找到相应的action object来执行。 layer相关的action的key 可以是被修改的属性名,也可以是标记action的指定字符串。当layer的一个合适的事件发生了,layer 调用 actionForKey方法来查找 关联key的action。在搜索过程中,你的app可以在几个点上进行插入,并为该键提供一个相关的action object。
Core Animation 在以下命令中查找 action objects:
  1. 如果layer有delegate而且 这个delegate 实现了 actionForLayer:forKey:方法,layer调用这个方法。delegate 必须做以下之一:
    • 返回给定key的action object
    • 如果它不处理action,返回nil,在这种情况下,搜索继续。
    • 返回NSNull object,这种情况下搜索立即结束。
  1. layer在layer的actions 字典中搜索给定的key
  2. layer在 style 字典里搜索包含key的 actions 字典。(换句话说,style 字典包含一个 actions key,他的值也是一个字典。layer 在这第二个字典里寻找给定的key)
  3. layer调用自己的 defaultActionForKey 类方法。
  4. layer执行被Core Animation定义的隐式action(如果有)
下图显示了一个实现 delegate方法,用来提供 action objects的代码。这个例子中,代理寻找 layer content 属性的变化,并使用transition animation将新内容转换成适当的位置。
Providing an action using a layer delegate object:
– (id<CAAction>)actionForLayer:(CALayer *)theLayer
forKey:(NSString *)theKey {
CATransition *theAnimation=nil;
if ([theKey isEqualToString:@”contents”]) {
theAnimation = [[CATransition alloc] init];
theAnimation.duration = 1.0;
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
theAnimation.type = kCATransitionPush;
theAnimation.subtype = kCATransitionFromRight;
}
return theAnimation;
}
使用 CATransaction 类临时禁用 Action
你可以使用 CATransaction 类来临时禁用 layer action。当你搞遍layer的属性是,Core Animation 经常创建一个隐式事务对象来动态化这个改变。如果你不想要动画,你可以通过创建一个显示transcaction,并且设置他的kCATransactionDisableActions 属性为true来禁用隐式动画。下图显示了禁用当layer从layer tree删除时动画的代码。
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
[aLayer removeFromSuperlayer];
[CATransaction commit];
提升动画性能
对于 OS X View,选择最佳的重绘策略
即使视图是layer backed的,NSView类的默认重绘策略也会保留该类的原始绘图行为。 如果您在应用中使用layer backed的视图,则应检查重绘策略选项并选择能够为您的应用提供最佳性能的视图。 在大多数情况下,默认策略不是可能提供最佳性能的策略。 相反,NSViewLayerContentsRedrawOnSetNeedsDisplayDisplay策略更有可能减少应用程序的绘制量并提高性能。 其他政策也可能为特定类型提供更好的表现。
OS X中更新 layers来优化你的绘制路径
在OS X v10.8及更高版本中,视图有两个用于更新底层layer内容的选项。 当您更新OS X v10.7及更早版本中的layer backed视图时,图层会将视图drawRect:方法中的绘图命令捕获到后台位图图像中。 缓存绘图命令是有效的,但在所有情况下都不是最有效的选项。 如果您知道如何直接提供图层的内容而不实际渲染它们,则可以使用updateLayer方法来实现。
iOS/OS X通用技巧
有许多让layer实现更有效的方法。但是,与任何此类优化一样,在尝试优化之前,您应该始终度量代码的当前性能。这为您提供了一个基线,您可以使用它来确定优化是否有效。

尽可能的使用不透明 layer

设置 opaque 属性YES到你的layer,能够让Core Animation知道不需要为这个layer配置aplha通道。没有alpha通道意味着 compositor组合器不需要 绑定layer 后面(background)的内容,这节省了绘制时间。但是,这个属性主要和 那些是由layer backed的视图组成的layer相关,或者是Core Animation创建底层位图的情况。如果你直接关联一个image到layer的content 属性,图片的alpha通道会被预先保留,不管 opaque 的值是什么。

对于 CAShapeLayer 对象,使用简单的path

CAShapeLayer类通过在合成阶段绘制你提供的path到一个bitmap来创建自己的内容。这个的优势是layer总是在最佳可能分辨率下绘制path,但是这个优势会带来更多的绘制时间消耗。如果你提供的path过于复杂,转换这些path的消耗可能会很昂贵。而且如果layer 的大小经常变化(因此需要频繁的被重绘),绘制所消耗的时间会叠加,造成性能瓶颈。
一个最小化 shape layer绘制时间的方法是把复杂的shapes打碎为简单shapes。使用更简单的路径、将多个CAShapeLayer对象层叠在一起,可以比绘制一条大的复杂路径快得多。因为绘制操作在CPU上执行,而组合任务在GPU上执行。但是,对于这种简化,潜在的性能收益其实取决于您的内容。因此,在优化之前测量代码的性能尤为重要,以便您有用于比较的基准。

Layer Size总是设置为整形 Integral

为获得最佳效果,请始终将图层对象的宽度和高度设置为整数值。虽然使用浮点数指定图层边界的宽度和高度,但最终将使用图层边界来创建位图图像。指定宽度和高度的整数值简化了Core Animation必须执行的工作,以创建和管理后备存储和其他图层信息。

如果需要,使用异步layer 绘制

你在delegate的drawLayer:inContext:方法中执行的任何绘制操作,或者你的视图的drawRect:方法,这俩操作通常在应用程序的主线程中同步发生。 但是,在某些情况下,同步绘制内容可能无法提供最佳性能。 如果您注意到您的动画效果不佳,则可以尝试在layer上启用drawsAsynchronously属性以将这些操作移至后台线程。 如果你这样做,确保你的绘图代码是线程安全的。 与往常一样,在将其放入生产代码之前,应始终测量绘图的性能。

当给layer添加shadow时,指定一个 shadow path

让Core Animation确定阴影的形状可能会很昂贵,并会影响应用程序的性能。 不是让Core Animation确定阴影的形状,而是使用CALayer的shadowPath属性明确指定阴影形状。 当您为此属性指定path object时,Core Animation将使用该形状绘制和缓存阴影效果。 对于形状永远不会改变或很少改变的图层,这可以通过减少Core Animation完成的渲染量来大大提高性能。
附录A: Layer Style Property Animations

附录B: Animatable Properties

附录C: Key-Value Coding Extensions

留下评论

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.