iOS核心动画
文章来源 好岸园IT技术学习网:http://www.hopean.com
前言,核心动画的好处
1.简单易用的高性能混合编程模型。
2.类似视图一样,你可以通过使用图层来创建复杂的接口。
3.轻量级的数据结构,它可以同时显示并让上百个图层产生动画效果。
4.一套简单的动画接口,可以让你的动画运行在独立的线程里面,并可以独立于主线程之外。
5.一旦动画配置完成并启动,核心动画完全控制并独立完成相应的动画帧。
6.提高应用性能。应用程序只当发生改变的时候才重绘内容。再小的应用程序也需要改变和提供布局服务层。核心动画还消除了在动画的帧速率上运行的应用程序代码。
7.灵活的布局管理模型。包括允许图层相对同级图层的关系来设置相应属性的位置和大小。
1.1 Core animation类
1.提供显示内容的图层类。CALayer
2.动画和计时类。Animation and Timing Classes
3.布局和约束类。
4.事务类,在原子更新的时候组合图层类。核心动画的基础类包含在 Quartz 核心框架(Quartz Core framework)里面,虽然它的其他图层类在其他框架里面定义。下图显示了核心动画的类层次结构。
层类(Layer Classes)
同一个视图类的实例一样,一个CAlayer实例也有一个单独的superlayer和上面所有的子层(sublayers),它创建了一个有层次结构的层,我们称之为layer tree。layers的绘制就像views一样是从后向前绘制的,绘制的时候我们要指定其相对与他们的superlayer的集合形状,同时还需要创建一个局部的坐标系。layers可以做一些更复杂的操作,例如rotate(旋转),skew(倾斜),scale(放缩),和project the layer content(层的投影)。
图层的内容提供
(1)直接设置层的content属性到一个core graphics图,或者通过delegation来设置
(2)提供一个代理直接绘制到Core Graphics image context(核心图形的上下文)
(3)设置任意数量的所有层共有的可视的风格属性。例如:backgroundColor(背景色),opacity(透明度)和masking(遮罩)。max os x应用通过使用core image filters来达到这种可视化的属性。
(4)子类化CAlayer,同时在更多的封装方式中完成上面的任意技术。
(1)CAScrollLayer:它是CALayer的一个子类,用来显示layer的某一部分,一个CAScrollLayer对象的滚动区域是由其子层的布局来定义的。CAScrollLayer没有提供键盘或者鼠标事件,也没有提供明显的滚动条。
(2)CATextLayer:它是一个很方便就可以从string和attributed string创建layer的content的类。
(3)CATiledLayer:它允许在增量阶段显示大和复杂的图像(就是将图形进行分块显示,来减少
1.1.2
动画和计时类(Animation and Timing Classes)
CAAnimation 实现了 CAMediaTiming 协议,提供了动画的持续时间,速度,和重复计数。 CAAnimation 也实现了 CAAction 协议。该协议为图层触发一个动画动作提供了提供标准化响应。动画类同时定义了一个使用贝塞尔曲线来描述动画改变的时间函数。例如,一个 匀速时间函数(linear timing function)在动画的整个生命周期里面一直保持速度不变, 而渐缓时间函数(ease-out timing function)则在动画接近其生命周期的时候减慢速度。核心动画额外提供了一系列抽象的和细化的动画类,比如:CATransition 提供了一个图层变化的过渡效果,它能影响图层的整个内容。 动画进行的时候淡入淡出(fade)、推(push)、显露(reveal)图层的内容。这些过渡效 果可以扩展到你自己定制的 Core Image 滤镜。CAAnimationGroup 允许一系列动画效果组合在一起,并行显示动画。
多个动画效果叠加,比如在执行动画的过程中需要同时修改position,alpha, frame等属性,可以将三个动画合成一起执行。CAAnimationGroup
animGroup.animations
animGroup.duration
[view.layer
CABasicAnimation 简单的为图层的属性提供修改。
1.1.3 布局管理器类Application Kit 的视图类相对于 superlayer 提供了经典的“struts and springs”定位 模型。图层类兼容这个模型,同时 Mac OS X 上面的核心动画提供了一套更加灵活 的布局管理机制,它允许开发者自己修改布局管理器。核心动画的 CAConstraint 类 是一个布局管理器,它可以指定子图层类限制于你指定的约束集合。每个约束 (CAConstraint 类的实例封装)描述层的几何属性(左,右,顶部或底部的边缘或水 平或垂直中心)的关系,关系到其同级之一的几何属性层或 superlayer。通用的布局管理器和约束性布局管理器将会在“布局核心动画的图层”部分讨论。
第二章 核心动画渲染框架
layer tree包含了每个layer的对象模型。当你为一个layer的属性设置一个的时候,他们的值就是你设置的。The presentation tree包含了当前正在呈现给用户作为动画发生的值。例如:对一个layer的backgroundcolor设置一个新的值的时候,在layer tree中的值会马上改变。然而,在presentation tree的相应层的backgroundcolor的值随着要显示给用户的插值颜色会被更新。当渲染一个layer的时候,the render-tree会使用presentation-tree的值。the
render-tree的责任就是执行独立与程序活动的合成操作;渲染是在一个单独进程或者线程中,以便使其对应用程序的run loop影响最小。当一个动画在执行的过程中,你可以查询相应的presentation layer的实例。如果你打算改变当前的动画并且从当前显示状态开始一个新的动画,这是非常有用的。
第三章
图层的坐标系在不同平台上面具有差异性。在 iOS 系统中,默认的坐标系统原点 在图层的中心左上角地方,原点向右和向下为正值。在 Mac OS X 系统中,默认的坐 标系原点在图层的中心左下角地方,原点向右和向上为正值。坐标系的所有值都是浮 点类型。你在任何平台上面创建的图层都采用该平台默认的坐标系。
每个图层定义并维护自己的坐标系,它里面的全部内容都由此相关的坐标系指定 位置。该准则同时适应于图层自己的内容和它的任何子图层。因为任何图层定义了它 自己的坐标系,CALayer 类提供相应的方法用于从一个图层坐标系的点、矩形、大小 值转化为另一个图层坐标系相应的值。一些基于图层的属性使用单元坐标空间测量它们的值。单元坐标空间指定图层边 界的相对值,而不是绝对值。单元坐标空间给定的 x 和 y 的值总是在 0.0 到 1.0 之间。 指定一个沿 X 轴的值为 0.0 的点,得到的是图层左边缘的一个点,而指定一个 1.0 的点,则是图层右边缘的一个点。(对 Y 轴而言,如果是在 iOS 系统,则 0.0 对应于 顶部的点,而 1.0 则是底部的点,而在 Mac OS X 系统,得到的刚好相反,就如之前 提到的坐标系不同一样)。而点(0.5,0.5)则刚好是图层的中心点。
虽然图层和图层树与视图和视图的结构在很多方面具有相似性,但是图层的几何 却不同,它更加简单通俗。图层的所有几何属性,包括图层的矩阵变换,都可以隐式 和显式动画。
图层的 position 属性是一个 CGPoint 的值,它指定图层相当于它父图层的位置, 该值基于父图层的坐标系。图层的 bounds 属性是一个 CGRect 的值,指定图层的大小(bounds.size)和图层的 原点(bounds.origin)。当你重写图层的重画方法的时候,bounds 的原点可以作为图形 上下文的原点。图层拥有一个隐式的 frame,它是 position,bounds,anchorPoint 和 transform 属性 的一部分。设置新的 frame 将会相应的改变图层的
position 和 bounds 属性,但是 frame 本身并没有被保存。但是设置新的 frame 时候,bounds 的原点不受干扰,bounds 的大 小变为 frame 的大小,即 bounds.size=frame.size。图层的位置被设置为相对于锚点 (anchor point)的适合位置。当你设置 frame 的值的时候,它的计算方式和 position、 bounds、和 anchorPoint 的属性相关。图层的 anchorPoint 属性是一个 CGPoint 值,它指定了一个基于图层
bounds 的符 合位置坐标系的位置。锚点(anchor point)指定了 bounds 相对于 position 的值,同 时也作为变换时候的支点。锚点使用单元空间坐标系表示,(0.0,0.0)点接近图层 的原点,而(1.0,1.0)是原点的对角点。改变图层的父图层的变换属性(如果存在 的话)将会影响到 anchorPoint 的方向,具体变化取决于父图层坐标系的 Y 轴。
下图描述了基于锚点的三个示例值:
anchorPoint 默认值是(0.5,0.5),位于图层边界的中心点(如上图显示),B 点 把 anchorPoint 设置为(0.0,0.5)。最后 C 点(1.0,0.0)把图层的 position 设置为 图层 frame 的右下角。该图适用于 Mac OS X 的图层。在 iOS 系统里面,图层使用不 同的坐标系,相应的(0.0,0.0)位于左上角,而(1.0,1.0)位于右下角。
图层的 frame、bounds、position 和 anchorPoint 关系如下图所示:
在该示例中,anchorPoint 默认值为(0.5,0.5),位于图层的中心点。图层的 position 值为(100.0,100.0),bounds 为(0.0,0.0,120,80.0)。通过计算得到图层的 frame为(40.0,60.0,120.0,80.0)。
如果你新创建一个图层,则只有设置图层的 frame 为(40.0,60.0,120.0,80.0),相应的 position 属性值将会自动设置为(100.0,100.0),而 bounds 会自动设置为 (0.0,0.0,120.0,80.0)。下图显示一个图层具有相同的 frame(如上图),但是在该图中它的 anchorPoint 属性值被设置为(0.0,0.0),位于图层的左下角位置。
图层的 frame 值同样为(40.0,60.0,120.0,80.0),bounds 的值不变,但是图层的 position 值已经改变为(40.0,60.0)。
图层的几何外形和 Cocoa 视图另外一个不同地方是,你可以设置图层的一个边角 的半径来把图层显示为圆角。图层的 cornerRadius 属性指定了重绘图层内容,剪切子 图层,绘制图层的边界和阴影的时候时候圆角的半径。
图层的 zPosition 属性值指定了该图层位于 Z 轴上面位置,zPosition 用于设置图层 相对于图层的同级图层的可视位置
3.3 图层的几何变换
图层一旦创建,你就可以通过矩阵变换来改变一个图层的几何形状。
CATransform3DIdentity 是单位矩阵,该矩阵没有缩放、旋转、歪斜、透视。把该 矩阵应用到图层上面,会把图层几何属性修改为默认值。
使用变换函数可以在核心动画里面在操作矩阵。你可以使用这些函数(如下表)去 创建一个矩阵一般后面用于改变图层或者它的子图层的 transform 和 sublayerTransform属性。变换函数或者直接操或者返回一个CATransform3D的数据结 构。这可以让你能够构建简单或复杂的转换,以便重复使用。
图层有一个图层树,既然是树,就允许添加,插入,删除,替换相应的图层,
下面将针对上面的知识做一个图片动画的demo
功能1 :移动图片到右下角
//向右下角缩小移动 - (IBAction)buttonClick:(id)sender { CGPoint fromPoint = imageView.center; //路径曲线 UIBezierPath *movePath = [UIBezierPath bezierPath]; [movePath moveToPoint:fromPoint]; CGPoint toPoint = CGPointMake(300, 460); [movePath addQuadCurveToPoint:toPoint controlPoint:CGPointMake(toPoint.x,fromPoint.y)]; //关键帧 CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; moveAnim.path = movePath.CGPath; moveAnim.removedOnCompletion = YES; //旋转变化 CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"]; scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; //x,y轴缩小到0.1,Z 轴不变 scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)]; scaleAnim.removedOnCompletion = YES; //透明度变化 CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"alpha"]; opacityAnim.fromValue = [NSNumber numberWithFloat:1.0]; opacityAnim.toValue = [NSNumber numberWithFloat:0.1]; opacityAnim.removedOnCompletion = YES; //关键帧,旋转,透明度组合起来执行 CAAnimationGroup *animGroup = [CAAnimationGroup animation]; animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim,opacityAnim, nil]; animGroup.duration = 1; [imageView.layer addAnimation:animGroup forKey:nil]; }
//向右边旋转 - (IBAction)rightRotateBtnClick:(id)sender { CGPoint fromPoint = imageView.center; UIBezierPath *movePath = [UIBezierPath bezierPath]; [movePath moveToPoint:fromPoint]; CGPoint toPoint = CGPointMake(fromPoint.x +100 , fromPoint.y) ; [movePath addLineToPoint:toPoint]; CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; moveAnim.path = movePath.CGPath; CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform"]; scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; //沿Z轴旋转 scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI,0,0,1)]; //沿Y轴旋转 // scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotatio n(M_PI,0,1.0,0)]; //沿X轴旋转 // scaleAnim.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI,1.0,0,0)]; scaleAnim.cumulative = YES; scaleAnim.duration =1; //旋转2遍,360度 scaleAnim.repeatCount =2; imageView.center = toPoint; scaleAnim.removedOnCompletion = YES; CAAnimationGroup *animGroup = [CAAnimationGroup animation]; animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, nil]; animGroup.duration = 2; [imageView.layer addAnimation:animGroup forKey:nil]; }
功能3
//图片旋转360度 - (IBAction)rota360BtnClick:(id)sender { CABasicAnimation *animation = [ CABasicAnimation animationWithKeyPath: @"transform" ]; animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; //围绕Z轴旋转,垂直与屏幕 animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI, 0, 0, 1.0) ]; animation.duration = 1; //旋转效果累计,先转180度,接着再旋转180度,从而实现360旋转 animation.cumulative = YES; animation.repeatCount = 2; //在图片边缘添加一个像素的透明区域,去图片锯齿 CGRect imageRrect = CGRectMake(0, 0,imageView.frame.size.width, imageView.frame.size.height); UIGraphicsBeginImageCont ext(imageRrect.size); [imageView.image drawInRect:CGRectMake(1,1,imageView.frame.size.width-2,imageView.frame.size.height-2)]; imageView.image = UIGraphicsGetImageFromCu rrentImageContext(); UIGraphicsEndImageContex t(); [imageView.layer addAnimation:animation forKey:nil]; }