iOS Developer - AnimationSwift开发CoreAnimation

CoreAnimation With Swift(1)--理论

2016-01-08  本文已影响482人  DevKyle

CoreAnimation

本次教程分为两个部分,分别为理论和实战,实战部分,主要就是一些代码,对理论部分的解析,下面让我们进入下面我们的第一部分CoreAnimation With Swift 理论

CALayer基础

UIView之所以能显示在屏幕上,完全是因为它内部的一个图层.当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示。CALayer是用来管理位图的state,以及如何被渲染出来。

CALayer基本属性

CALayeranchorpoint是图层的关键,anchorpoint的位置发生变化,包括CALayerposition等属性相应的就会发生变化。anchorpoint是用单位坐标的(x,y轴都是从0到1,默认的anchorpoint的坐标为(0.5,0.5))

position:CGPoint用来设置CALayer在父层中的位置
anchorPoint:CGPoint决定着CALayer身上的哪个点会在position属性所指的位置

contentsGravity

当图片大小不是适合当前view的时候,你可能会使用view.contentMode来设置缩放和位置,其实他是通过控制CALayercontentGravity属性来实现的

contentsScale

由于layer不能区分分辨率,contentScale用来定义像素尺寸到layer尺寸的拉伸比率,默认值是1

contentsRect

contentsRect使用的是单位坐标,表示的是需要显示的内容的尺寸

contentsCenter

contentsCenter:CGRect定义了layer内部一个可以被拉伸的区域,外部则是一个固定的边界。这个属性将一个layer分为9个部分。根据contentsGravity的设定,拉伸相应地区域。可以参考微信聊天记录上那个气泡

layer的视觉效果

圆角

CALayer有一个cornerRadius属性用来制作layer的圆角,默认情况下该属性只作用于backgroundColor,而对于sublayer和背景图都没有效果,但是如果配合masksToBounds=YES的设置,可以对layer内所有元素生效。

边框

CALayer通过borderWidthborderColor两个属性的组合来定义border的宽度和颜色

阴影

通过设置shadowOpacity属性为一个大于0的值,可以给任意layer添加一个背景阴影。取值范围是0.0到1.0,表示全透明到非透明。同时还可以通过shadowColor(默认为黑)、shadowOffset(距离和方向)和shadowRadius(模糊度)三个属性来修改阴影的外观。不同于border围绕在layer的bounds,shadow是围绕在真正的内容外的。可以定义shadowPath来自定义阴影路径

遮罩(Masking)

mask属性接受另外一个CALayer值,可以被用作将当前layer裁切成给定layer的轮廓。masksToBounds则是让子视图不超过父视图的范围

Opacity

这里要说得是当子图层和父图层都设置了Opacity的时候(比如父类为0.5,子类为0.5 ,那么实际上子类的透明度为0.5*0.5+0.5(父类)=0.75).而下面是你设置透明度和alpha等需要注意的
1.opaque 直接不再绘制这层layer下面的layer(效率高)
2.alpha会影响在layer上视图的透明度

  1. 在app的Info.plist中添加UIViewGroupOpacity=YES。这会作用于当前app下的所有范围,可能还有点小的性能浪费。
  2. 通过CALayershouldRasterize=YES,将layer及其子layer全部打散成一个平面的图形,再使Opacity生效就可以达到我们想要的效果了。由于rasterizationScale默认等于1.0,会使得高清屏幕下图片像素化,所以我们还要同时设置一下:layer.rasterizationScale = UIScreen.mainScreen().scale

Transforms

UIViewtransform属性类型是GAffineTransform,它用于对view进行二维空间下的旋转、拉伸或变形。其实他就是对CALayer的封装。CALayer同时也有一个transform属性,它的类型是CATransform3DCATransform3D是一个4x4的矩阵,从而支持了对于垂直屏幕的z轴的变换效果。其中m34属性通常被用来计算x和y值的缩放比例,默认值是0)。CALayer实现仿射变化的属性是affineTransform(UIView就是对这个的封装)。CATransform3D的定义和它各个参数的作用如下。

struct CATransform3D {
  m11(x缩放), m12(y切变),m13(旋转),  m14();
  m21(x切变), m22(y缩放), m23(),    m24();
   m31(旋转),  m32( ),    m33(), 
 m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义)
 m41(x平移),     m42(y平移),     m43(z平移),     m44();
 };

CATransform3D相关的函数

CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz);
t:就可以理解为:函数的叠加,效果的叠加。
CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz);
sx:X轴缩放,代表一个缩放比例,一般都是 0 --- 1 之间的数字。sy:Y轴缩放。
sz:整体比例变换时,也就是m11(sx)== m22(sy)时,若m33(sz)>1,图形整体缩小,若0<1,图形整体放大,若m33(sz)<0,发生关于原点的对称等比变换。
CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
旋转效果。angle:旋转的弧度,所以要把角度转换成弧度:角度 * M_PI / 180。
x:向X轴方向旋转。值范围-1 --- 1之间y:向Y轴方向旋转。值范围-1 --- 1之间
z:向Z轴方向旋转。值范围-1 --- 1之间

CALayer的子类

CAGradientLayer

实现CALayer的渐变色

 // 渐变图层
    let gradientL = CAGradientLayer()
    gradientL.frame = bottomView.bounds;
    gradientL.opacity = 0;
    gradientL.colors = @[UIColor.clearColor().CGColor,UIColor blackColor().CGColor];

CAReplicatorLayer

CAReplicatorLayer复制图层,把子图层放在CAReplicatorLayer中,,可以把图层里面所有子层复制.这个图层用于,有许多相同的子视图做相同的动画效果的时候,这个可以复制这些子视图,并且为这些子视图加上延迟等效果

  let repL = CAReplicatorLayer()
  repL.frame = _lightView.bounds;
  _lightView.layer.addSublayer(repL)
  // 子图层
  let layer = CALayer()
  layer.anchorPoint = CGPointMake(0.5, 1)
  layer.position = CGPointMake(15, _lightView.bounds.size.height)
  layer.bounds = CGRectMake(0, 0, 30, 150)
  repL.addSublayer(layer)
  let anim = CABasicAnimation()
  anim.keyPath = "transform.scale.y"
  anim.toValue = 0.1
  anim.duration = 0.5
  anim.repeatCount = MAXFLOAT;
  // 设置动画反转
  anim.autoreverses = YES;
  layer.addAnimation(anim,forKey:nil)
  // 复制层中子层总数
  // instanceCount:表示复制层里面有多少个子层,包括原始层
  repL.instanceCount = 3;
  // 设置复制子层偏移量,不包括原始层,相对于原始层x偏移,instanceTransform决定如何排序
  repL.instanceTransform = CATransform3DMakeTranslation(45, 0, 0);
  // 设置复制层动画延迟时间
  repL.instanceDelay = 0.1;
  // 如果设置了原始层背景色,就不需要设置这个属性
  repL.instanceColor = UIColor.greenColor().CGColor
  repL.instanceGreenOffset = -0.3;

UIView和CALayer的选择

其实,对比CALayerUIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以.所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要跟用户进行交互,用UIView或者CALayer都可以当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级。虽然CALayer不感知responder chain,但是他通过提供-containPoint:-hitTest:两个方法来帮助你定位事件所在layer。
我们为什么要了解CALayer的存在,因为他有一些UIView不能做的事情:

  1. 阴影、圆角、多彩的border
  2. 3D变化和定位
  3. 非矩形的边界
  4. alpha遮罩
  5. 多步非线性动画

layer的绘制

在UIView的子类中可以通过实现-drawRect:方法来自定义绘图,CALayer的自定义绘图则通过layer的delegate对象(CALayerDelegate 协议)的以下两个方法来实现:

     // 方法1 需要手动调用layer.display()方法来调用
             func displayLayer(_ layer: CALayer!);

    // 方法2. 如果方法1没有实现,则调方法2。需要手动调用layer.display()方法来调用
  func drawLayer(_ layer: CALayer!, inContext  ctx,:  CGContext )

layer tree

CoreAnimation是一个合成引擎,它的工作是尽可能快的将不同的可视化的内容合成到屏幕上。这个可视化内容是不同的层(layer),构成一个叫做层树(layer tree)的层次结构。层树主要由三个部分组成:

layer tree.png
  1. 模型层树:app交互的地方,存储目标值的地方
  2. 外观层树:展示值得地方(从origin到target),在这一层获取动画过程中的当前值,这一层通过presentationLayer()方法获取。这层就是所看到的动画,但是实际上控件上的视图的frame之类的属性没有发生变化。
  3. 渲染层树:私有的方法

隐式动画

每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)。所有的非Root Layer(也就是手动创建的CALayer对象),都存在着隐式动画,也就是说当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果而这些属性称为Animatable Properties(可动画属性)常见的Animatable Properties:

  1. bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
  2. backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
  3. position:用于设置CALayer的位置。修改这个属性会产生平移动画

当然这个隐式的动画可以通过动画事务(CATransaction)关闭,它负责成批的把多个图层树的修改作为一个原子更新到渲染树。

核心动画

核心动画继承结构

核心动画的继承关系如下

屏幕快照 2016-06-26 01.12.04.png
从上图中可以看到核心动画中所有类都遵守CAMediaTiming。CAAnaimation是所有动画类的父类,是个抽象类,不具备动画效果.CAAnimationGroupCATransition才有动画效果,CAAnimationGroup是个动画组,可以同时进行缩放,旋转。CATransition是转场动画,界面之间跳转都可以用转场动画。CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类才有.CABasicAnimation基本动画,做一些简单效果CAKeyframeAnimation帧动画,做一些连续的流畅的动画.给一个图层添加动画的步骤为如下
  1. 创建CALayer
  2. 初始化一个CAAnimation对象,并设置一些动画相关属性
  3. 通过调用CALayeraddAnimation:forKey:方法,增加CAAnimation对象到CALayer中,这样就能开始执行动画了
  4. 通过调用CALayerremoveAnimationForKey:方法可以停止CALayer中的动画

CAAnimation的属性介绍

CAAnimation是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用.下面需要对它的一些属性的介绍

CAMediaTiming协议的属性

  1. duration:动画的持续时间
  2. repeatCount:重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT
  3. repeatDuration:重复时间
  4. fillMode:决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之后. 因为动画是在presenting tree上展现的,动画结束后是展现的模型树,presenting tree将被移除。
  5. beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间

CAAnimation的属性

  1. removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
  2. timingFunction:速度控制函数,控制动画运行的节奏如:kCAMediaTimingFunctionLinear等
  3. delegate:动画代理(CAAnimationDelegate)代理方法监听动画的开始结束等状态

CABasicAnimation

随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue.keyPath内容是CALayer的可动画Animatable属性如果fillMode=kCAFillModeForwardsremovedOnComletion=false,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。(只是没有移除掉presenting tree)

**注意:核心动画一切都是假象,并不会真实的改变图层的属性值,如果以后做动画的时候,不需要与用户交互,通常用核心动画(转场)。UIView动画必须通过修改属性的真实值,才有动画效果。
**

CAKeyframeAnimation

CAKeyframeAnimation会使用一个NSArray保存动画的属性数值

  1. values:上述的NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
  2. path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayeranchorPointposition起作用。如果设置了path,那么values将被忽略
  3. keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

CAAnimationGroup

CAAnimationGroup可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行,主要的属性有

  1. animations:用来保存一组动画对象的Array默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间

CATransition

CATransition用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果.主要的动画属性有:

  1. type:动画过渡类型
  2. subtype:动画过渡方向
  3. startProgress:动画起点(在整体动画的百分比)
  4. endProgress:动画终点(在整体动画的百分比)

type属性控制着转场动画过渡效果

转场动画过渡效果

可以通过

+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

实现单视图的转场,而两个视图则得通过下面的方法

+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion

定时器

当你需要定时的执行同一个效果的动画的时候,就需要使用定时器,iOS中有两种定时器,NSTimerCADisplayLinkNSTimer就是普通意义上的定时器,下面主要是讲CADisplayLink

CADisplayLink

CADisplayLink是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右,可以使绘图代码与视图的刷新频率保持同步,而NSTimer无法确保计时器实际被触发的准确时间,一个CADisplayLink实例对象,会根据更新率定时同步绘画内容。需要提供一个选择器,当更新内容的时候。这个需要添加到一个RunLoop当中的去(相当是添加了定时器类型的Runloop)。
具体的使用使用方法:

  1. 定义CADisplayLink并制定触发调用方法
  2. 将显示链接添加到主运行循环队列

CADisplayLink相关的属性有

  1. duration:每帧之间的时间
  2. frameInterval:就是间隔多少帧调用一次选择器。,默认值是1.
  3. paused: 是否暂停当前的定时器,控制 CADisplayLink的运行
  4. timestamp:上一帧结束时候的时间,下一帧需要知道什么内容将被呈现

由于 CADisplayLink 绑定的方法会在每次屏幕刷新时被调用,精确度相当之高。正是基于这个特点,CADisplayLink 非常适合 UI 的重绘。

上一篇下一篇

猜你喜欢

热点阅读