版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.09.23 |
前言
app中好的炫的动画可以让用户耳目一新,为产品增色不少,关于动画的实现我们可以用基本动画、关键帧动画、序列帧动画以及基于CoreGraphic的动画等等,接下来这几篇我就介绍下我可以想到的几种动画绘制方法。具体Demo示例已开源到Github —— 刀客传奇,感兴趣的可以看我写的另外几篇。
1. 实现动画方式深度解析(一) —— 播放GIF动画(一)
2. 实现动画方式深度解析(二) —— 播放GIF动画之框架FLAnimatedImage的使用(二)
3. 实现动画方式深度解析(三) —— 播放序列帧动画(一)
4. 实现动画方式深度解析(四) —— QuartzCore框架(一)
5. 实现动画方式深度解析(五) —— QuartzCore框架之CoreAnimation(二)
6. 实现动画方式深度解析(六) —— Core Animation Basics(三)
7. 实现动画方式深度解析(七) —— Core Animation之Setting Up Layer Objects(四)
8. 实现动画方式深度解析(八) —— Core Animation之动画层内容 (五)
Building a Layer Hierarchy - 构建图层层次结构
大多数情况下,在应用程序中使用图层的最佳方法是将它们与视图对象结合使用。 但是,有时您可能需要通过向其添加其他图层对象来增强视图层次结构。 在这样做时,您可以使用使用层提供更好的性能,或者让您实现一个单独使用视图很难处理的功能。 在这种情况下,您需要知道如何管理您创建的层次结构。
重要提示:在OS X v10.8及更高版本中,建议您最小化对层次结构的使用,并仅使用层支持的视图。 在该版本的OS X中引入的层重绘策略允许您自定义层次支持的视图的行为,并且仍然可以获得之前使用独立图层获得的那种性能。
Arranging Layers into a Layer Hierarchy - 将层布置到层次结构中
层次layer结构在很多方面都是和视图view结构类似的。 您将一层嵌入另一层以在嵌入层(称为子层)与父层(称为超层)之间创建亲子关系。 这个亲子关系影响子层的各个方面。 例如,其内容位于其父级的内容之上,其位置相对于其父级的坐标系来指定,并且受到应用于父级的任何转换的影响。
1. Adding, Inserting, and Removing Sublayers - 增加、插入和删除子层
每个层对象都有添加,插入和删除子层的方法。 下表总结了这些方法及其行为。
行为 | 方法 | 描述 |
---|---|---|
增加layer | addSublayer: | 向当前图层添加一个新的子图层对象。 子层被添加到层的子层列表的末尾。 这会导致子层在其zPosition属性中以相同的值出现在任何兄弟图层之上 |
插入layer | insertSublayer:above: insertSublayer:atIndex: insertSublayer:below: | 在指定的索引或相对于另一个子层的位置插入子层到子层层次结构中。 当插入另一个子层的上方或下方时,您只在子层数组中指定子层的位置。 层的实际可见性主要由其zPosition属性中的值确定,其次由它们在子层数组中的位置确定。 |
移除layer | removeFromSuperlayer | 从父层中移除子层 |
交换layer | replaceSublayer:with: | 交换一个子层与另一个子层。 如果要插入的子层已经在另一个层次结构中,则会从该层次结构中先删除它。 |
使用您自己创建的图层对象时,可以使用上述方法。 您不会使用这些方法来排列属于图层支持视图的图层。 但是,层次支持的视图可以作为您自己创建的独立图层的父级。
2. Positioning and Sizing Sublayers - 定位和调整子层
添加和插入子层时,您必须在子屏幕出现之前设置子层的大小和位置。 在添加到层次结构中之后,您可以修改子层的大小和位置,但是在创建层时应该习惯设置这些值。
您使用bounds属性设置子层的大小,并使用position属性将其位置设置在其超层内。 边界矩形的起点几乎总是(0,0),而大小是您为点指定的层所需的任何大小。 位置属性中的值相对于默认情况下位于图层中心的图层的锚点进行解释。 如果不为这些属性分配值,Core Animation将图层的初始宽度和高度设置为0,并将位置设置为(0,0)。
myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);
重要提示:始终对图层的宽度和高度使用整数。
3. How Layer Hierarchies Affect Animations - 图层层级是如何影响到动画的
某些超层的属性可能会影响应用于其子层的任何动画的行为。 一个这样的属性是速度属性,它是动画速度的乘数。 此属性的值默认设置为1.0,但将其更改为2.0会导致动画以原始速度的两倍运行,从而在一半时间内完成。 此属性不仅影响其设置的图层,还会影响该图层的子图层。 这种变化也是乘法的。 如果子层及其超层速度为2.0,则子层上的动画以其原始速度的四倍运行。
大多数其他层次更改可以以可预测的方式影响任何包含的子层。 例如,将旋转变换应用于层将旋转该层及其所有子层。 类似地,更改图层的不透明度会改变其子图层的不透明度。 对图层大小的更改遵循Adjusting the Layout of Your Layer Hierarchies中描述的布局规则。
Adjusting the Layout of Your Layer Hierarchies - 调整层次结构的布局
核心动画支持几个选项来调整子层的大小和位置以响应其超级层的更改。 在iOS中,普遍使用层次支持的视图使得创建层次结构不太重要; 仅支持手动布局更新。 对于OS X,还有其他几个选项可以帮助您更轻松地管理层次结构。
如果您使用创建的独立图层对象构建图层层次结构,则层级布局才是相关的。 如果您的应用程序的图层与视图相关联,请使用基于视图的布局支持来更新视图的大小和位置以响应更改。
1. Using Constraints to Manage Your Layer Hierarchies in OS X - 在OS X中使用约束管理您的图层层级
约束允许您使用层与其上层或同层之间的一组详细关系来指定层的位置和大小。 定义约束需要以下步骤:
- 创建一个或多个CAConstraint对象。 使用这些对象来定义约束参数。
- 将约束对象添加到其修改属性的图层上。
- 检索共享的CAConstraintLayoutManager对象并分配给直接的上层。
下图显示了可用于定义约束的属性以及它们影响的图层的方面。 您可以使用约束来根据相对于另一个图层的中点边缘的位置来更改图层的位置。 您也可以使用它们来更改图层的大小。 您所做的更改可能与父层成正比或与另一层相关。 您甚至可以为结果更改添加缩放因子或常数。 这种额外的灵活性使得可以使用一组简单的规则非常精确地控制图层的大小和位置。
每个约束对象沿同一个轴封装两个层之间的一个几何关系。 可以将最多两个约束对象分配给每个轴,并且它们是确定哪个属性是可更改的那些约束。 例如,如果为图层的左右边缘指定约束,则图层的大小将更改。 如果您为图层的左边缘和宽度指定约束,则图层右边缘的位置将更改。 如果为其中一个边缘指定单个约束,则Core Animation将创建一个隐含的约束,该约束可将图层的大小固定在给定维中。
创建约束时,必须始终指定三条信息:
- 要限制的层的方面
- 该层用作参考
- 参考层的方面用于比较
下面代码显示了一个简单的约束,将一个图层的垂直中点定位到其上层的垂直中点。 当引用超级层时,使用字符串超层。 这个字符串是一个专用名称,用于引用超级层。 使用它不需要有指向图层的指针或知道图层的名称。 它还允许您更改超级层,并将约束自动应用于新的父级。 (当创建相对于兄弟层的约束时,您必须使用其name属性来标识兄弟层。
//Defining a simple constraint
[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
relativeTo:@"superlayer"
attribute:kCAConstraintMidY]];
要在运行时应用约束,必须将共享的CAConstraintLayoutManager
对象附加到直接的上层。 每层负责管理子层的布局。 将布局管理器分配给父级会告诉Core Animation
应用其子节点定义的约束。 布局管理器对象自动应用约束。 在将其分配给父层之后,您不需要告诉它来更新布局。
要了解限制在更具体的情况下如何工作,请参见下图。 在该示例中,设计要求层A的宽度和高度保持不变,并且层A在其超层内保持居中。 另外,层B的宽度必须与层A的宽度一致,层B的顶边必须保持在层A的底边下方10点,层B的底边必须重新指向超层底部边缘下10点。 下面代码显示了您将用于创建此示例的子层和约束的代码。
// Setting up constraints for your layers
// 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];
关于上面代码的一个有趣的事情是代码不会明确地设置layerB的大小。 由于定义的约束,每次布局更新时都会自动设置layerB的宽度和高度。 因此,使用边界矩形设置大小是不必要的。
警告:创建约束时,不要在约束之间创建循环引用。 循环约束使得无法计算所需的布局信息。 遇到这样的循环引用时,布局行为是未定义的。
2. Setting Up Autoresizing Rules for Your OS X Layer Hierarchies - 为您的OS X层次结构设置自动调整规则
自动调整规则是在OS X中调整图层大小和位置的另一种方法。通过自动调整规则,您可以指定图层的边缘是否应保持与超层相应边缘固定或可变距离。 您可以类似地指定图层的宽度或高度是固定的还是可变的。 关系总是在层和它的超层之间。 您不能使用自动调整规则来指定兄弟层之间的关系。
3. Manually Laying Out Your Layer Hierarchies - 手动布局层次结构
在iOS和OS X上,您可以通过在超级层的委托对象上实现layoutSublayersOfLayer:
方法来手动处理布局。 您可以使用该方法来调整当前嵌入在图层中的任何子图层的大小和位置。 当进行手动布局更新时,由您执行必要的计算来定位每个子层。
如果要实现自定义层子类,则您的子类可以覆盖layoutSublayers
方法,并使用该方法(而不是委托)来处理任何布局任务。 在您需要完全控制自定义图层类中子图层的位置的情况下,才应该覆盖此方法。 替换默认实现会阻止Core Animation在OS X上应用约束或自动调整规则。
Sublayers and Clipping - 子图层和裁剪
与视图不同,超层不会自动剪贴位于其边界矩形之外的子图层的内容。 相反,默认情况下,超级层允许其子层完全显示。 但是,您可以通过将图层的masksToBounds
属性设置为YES来重新启用裁剪。
层的剪裁蒙版的形状包括层的角半径,如果指定了它的角半径。 下图显示了一个层,它演示了maskToBounds
属性如何影响具有圆角的图层。 当属性设置为NO时,子图层将全部显示,即使它们超出了其父层的边界。 将属性更改为YES会导致其内容被剪切。
Converting Coordinate Values Between Layers - 层之间转换坐标值
偶尔,您可能需要将一层中的坐标值转换为不同图层中相同屏幕位置的坐标值。 CALayer类提供了一组可用于此目的的简单转换程序:
convertPoint:fromLayer:
convertPoint:toLayer:
convertRect:fromLayer:
convertRect:toLayer:
除了转换点和矩形值之外,还可以使用convertTime:fromLayer:和convertTime:toLayer:方法来转换图层之间的时间值。 每个层定义自己的本地时间空间,并使用该时间空间将动画的开始和结束与系统的其余部分同步。 这些时间空间默认同步; 但是,如果更改一组图层的动画速度,那么这些图层的时间空间将相应更改。 您可以使用时间转换方法来解决任何这些因素,并确保两个层的时间同步。
后记
未完,待续~~