iOS动画(CoreAnimation)

一、iOS核心动画介绍

核心动画框架

CoreAnimation框架是基于OpenGL与CoreGraphics图像处理框架的一个跨平台的动画框架。
在CoreAnimation中大部分的动画都是通过Layer层来实现的,通过CALayer,我们可以组织复杂的层级结构。
在CoreAnimation中,大多数的动画效果是添加在图层属性的变化上,例如,改变图层的位置,大小,颜色,圆角半径等。Layer层并不决定视图的展现,它只是存储了视图的几何属性状态。

1. 首先通过 Core Animation 类图了解下各个类之间的关系.

核心动画类组织框架

CAAnimation:核心动画的基础类,不能直接使用,负责动画运行时间、速度的控制,本身实现了CAMediaTiming协议。
CAAnimationGroup:动画组,动画组是一种组合模式设计,可以通过动画组来进行所有动画行为的统一控制,组中所有动画效果可以并发执行。
CAPropertyAnimation:属性动画的基类(通过属性进行动画设置,注意是可动画属性),不能直接使用。
CATransition:转场动画,主要通过滤镜进行动画效果设置。
CABasicAnimation:基础动画,通过属性修改进行动画参数控制,只有初始状态和结束状态。
CAKeyframeAnimation:关键帧动画,通过属性进行动画参数控制,可以有多个状态控制。
CASpringAnimation:弹簧动画,通过属性进行动画参数控制,可以有多个状态控制。但是9.0才有的属性。

基础动画、关键帧动画都属于属性动画,就是通过修改属性值产生动画效果,开发人员只需要设置初始值和结束值,中间的过程动画(又叫“补间动画”)由系统自动计算产生。和基础动画不同的是关键帧动画可以设置多个属性值,每两个属性中间的补间动画由系统自动完成。

2.接下来详细介绍下动画的各个属性及作用

  • fromValue : 动画的开始值(Any类型, 根据动画不同可以是CGPoint、NSNumber等)

  • toValue: 动画的结束值, 和fromValue类似

  • beginTime: 动画的开始时间

  • duration : 动画的持续时间

  • repeatCount : 动画的重复次数

  • fillMode: 动画的运行场景

  • isRemovedOnCompletion: 完成后是否删除动画

  • autoreverses: 执行的动画按照原动画返回执行

  • path:关键帧动画中的执行路径

  • values: 关键帧动画中的关键点数组

  • animations: 组动画中的动画数组

  • delegate : 动画代理, 封装了动画的执行和结束方法

  • timingFunction: 控制动画的显示节奏, 系统提供五种值选择,分别是:

    1. kCAMediaTimingFunctionDefault( 默认,中间快)
    2. kCAMediaTimingFunctionLinear (线性动画)
    3. kCAMediaTimingFunctionEaseIn (先慢后快 慢进快出)
    4. kCAMediaTimingFunctionEaseOut (先块后慢快进慢出)
    5. kCAMediaTimingFunctionEaseInEaseOut (先慢后快再慢)
  • type: 过渡动画的动画类型,系统提供了多种过渡动画, 分别是:

    1. fade (淡出 默认)
    2. moveIn (覆盖原图)
    3. push (推出)
    4. fade (淡出 默认)
    5. reveal (底部显示出来)
    6. cube (立方旋转)
    7. suck (吸走)
    8. oglFlip (水平翻转 沿y轴)
    9. ripple (滴水效果)
    10. curl (卷曲翻页 向上翻页)
    11. unCurl (卷曲翻页返回 向下翻页)
    12. caOpen (相机开启)
    13. caClose (相机关闭)
  • subtype : 过渡动画的动画方向, 系统提供了四种,分别是:

    1. fromLeft( 从左侧)
    2. fromRight (从右侧)
    3. fromTop (有上面)
    4. fromBottom (从下面)
  • mass: 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大

  • stiffness:弹簧动画的刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快

  • damping:阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快

  • initialVelocity:初始速率,动画视图的初始速度大小
    速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反

  • settlingDuration:结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
    通常弹簧动画的时间使用结算时间比较准确

二、Core Animation的使用

1. 基础动画(CABasicAnimation

基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、旋转、缩放、透明度、背景色等。
基础动画根据 keyPath 来区分不同的动画, 系统提供了多个类型,如:
基础动画根据 keyPath 来区分不同的动画, 系统提供了多个类型,如: transform.scale (比例转换)、transform.scale.xtransform.scale.ytransform.rotation(旋转) 、transform.rotation.x(绕x轴旋转)、transform.rotation.y(绕y轴旋转)、transform.rotation.z(绕z轴旋转)、opacity (透明度)、marginbackgroundColor(背景色)、cornerRadius(圆角)、borderWidth(边框宽)、boundscontentscontentsRectcornerRadiusframehiddenmaskmasksToBoundsshadowColor(阴影色)、shadowOffsetshadowOpacity, 在使用时候, 需要根据具体的需求选择合适的。

旋转.gif
  • 位移动画:
 func positionAnimation() {
        
        let animation = CABasicAnimation.init(keyPath: "position") //keyPath为系统提供
        animation.fromValue = CGPoint.init(x: margin_ViewMidPosition, y: kScreenH / 2 - margin_Top)
        animation.toValue = CGPoint.init(x: kScreenW - margin_ViewMidPosition, y: kScreenH / 2 - margin_Top)
        animation.duration = 1.0
        view_Body.layer.add(animation, forKey: "positionAnimation") //key自定义
}
  • 旋转动画:
func rotateAnimation() {
        
        let animation = CABasicAnimation.init(keyPath: "transform.rotation.z")
        animation.toValue = NSNumber.init(value: Double.pi)
        animation.duration = 0.1
        animation.repeatCount = 1e100 //无限大重复次数
        view_Body.layer.add(animation, forKey: "rotateAnimation")
 }
  • 缩放动画:
 func scaleAnimation() {
        
        let animation = CABasicAnimation.init(keyPath: "transform.scale")
        animation.toValue = NSNumber.init(value: 2.0)
        animation.duration = 1.0
        view_Body.layer.add(animation, forKey: "scaleAnimation")
}
  • 透明度动画:
func opacityAnimation() {
        
        let animation = CABasicAnimation.init(keyPath: "opacity")
        animation.fromValue = NSNumber.init(value: 1.0)
        animation.toValue = NSNumber.init(value: 0.0)
        animation.duration = 1.0
        view_Body.layer.add(animation, forKey: "opacityAnimation")
}
  • 背景色动画
 func backgroundColorAnimation() {
        
        let animation = CABasicAnimation.init(keyPath: "backgroundColor")
        animation.toValue = UIColor.green.cgColor //因为layer层动画, 所以需要使用cgColor
        animation.duration = 1.0
        view_Body.layer.add(animation, forKey: "backgroundColorAnimation")
    }

2. 关键帧动画( CAKeyframeAnimation )

CAKeyframeAnimationCABasicAnimation 都属于CAPropertyAnimatin 的子类。不同的是 CABasicAnimation 只能从一个数值(fromValue)变换成另一个数值(toValue),而 CAKeyframeAnimation 则会使用一个数组(values) 保存一组关键帧, 也可以给定一个路径(path)制作动画。

CAKeyframeAnimation主要有 三个 重要属性:

  • values:存放关键帧(keyframe)的数组,动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 .
  • path:可以设置一个 CGPathRefCGMutablePathRef,让层跟着路径移动. path 只对 CALayer 的 anchorPointposition 起作用, 如果设置了path,那么values将被忽略.
  • keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0, keyTimes 中的每一个时间值都对应 values 中的每一帧.当 keyTimes 没有设置的时候,各个关键帧的时间是根据 duration 平分的。

以抖动截图为例, 效果图如下:

抖动.gif

动画代码如下:

  • 关键帧动画
func keyFrameAnimation() {
        
        let animation = CAKeyframeAnimation.init(keyPath: "position")
        let value_0 = CGPoint.init(x: margin_ViewMidPosition, y: kScreenH / 2 - margin_ViewWidthHeight)
        let value_1 = CGPoint.init(x: kScreenW / 3, y: kScreenH / 2 - margin_ViewWidthHeight)
        let value_2 = CGPoint.init(x: kScreenW / 3, y: kScreenH / 2 + margin_ViewMidPosition)
        let value_3 = CGPoint.init(x: kScreenW * 2 / 3, y: kScreenH / 2 + margin_ViewMidPosition)
        let value_4 = CGPoint.init(x: kScreenW * 2 / 3, y: kScreenH / 2 - margin_ViewWidthHeight)
        let value_5 = CGPoint.init(x: kScreenW - margin_ViewMidPosition, y: kScreenH / 2 - margin_ViewWidthHeight)
        animation.values = [value_0, value_1, value_2, value_3, value_4, value_5]
        animation.duration = 2.0
        view_Body.layer.add(animation, forKey: "keyFrameAnimation")
}
  • 路径动画
func pathAnimation() {
        
        let animation = CAKeyframeAnimation.init(keyPath: "position")
        let path = UIBezierPath.init(arcCenter: CGPoint.init(x: kScreenW / 2, y: kScreenH / 2), radius: 60, startAngle: 0.0, endAngle: .pi * 2, clockwise: true)
        animation.duration = 2.0
        animation.path = path.cgPath
        view_Body.layer.add(animation, forKey: "pathAnimation")
}
  • 抖动动画
func shakeAnimation() {
        
        let animation = CAKeyframeAnimation.init(keyPath: "transform.rotation")
        let value_0 = NSNumber.init(value: -Double.pi / 180 * 8)
        let value_1 = NSNumber.init(value: Double.pi / 180 * 8)
        animation.values = [value_0, value_1, value_0]
        animation.duration = 1.0
        animation.repeatCount = 1e100
        view_Body.layer.add(animation, forKey: "shakeAnimation")
}

4. 弹簧动画( CASpringAnimation )

iOS9才引入的动画类,它继承于CABaseAnimation,用于制作弹簧动画,先演示个例子

弹簧动画.gif

动画代码如下:

func springAnimation() {
        CASpringAnimation *spring = CASpringAnimation(keyPath: @"position.x");
        spring.damping = 5;
        spring.stiffness = 100;
        spring.mass = 1;
        spring.initialVelocity = 0;
        spring.fromValue = label.layer.position.x;
        spring.toValue = label.layer.position.x + 50;
        spring.duration = spring.settlingDuration;
        label.layer.addAnimation(spring, forKey: spring.keyPath);
}

把 label 替换成你要进行动画的视图,然后完成一个弹簧动画了

3. 组动画( CAAnimationGroup )

CAAnimationGroupCAAnimation 的子类,可以保存一组动画对象,可以保存基础动画、关键帧动画等,数组中所有动画对象可以同时并发运行, 也可以通过实践设置为串行连续动画.

效果截图如下:

组动画.gif

动画代码如下:

  • 同时
//同时
func sameTimeAnimation() {
        
        let animation_Position = CAKeyframeAnimation.init(keyPath: "position")
        let value_0 = CGPoint.init(x: margin_ViewMidPosition, y: kScreenH / 2 - margin_ViewMidPosition)
        let value_1 = CGPoint.init(x: kScreenW / 3, y: kScreenH / 2 - margin_ViewMidPosition)
        let value_2 = CGPoint.init(x: kScreenW / 3, y: kScreenH / 2 + margin_ViewMidPosition)
        let value_3 = CGPoint.init(x: kScreenW / 3 * 2, y: kScreenH / 2 + margin_ViewMidPosition)
        let value_4 = CGPoint.init(x: kScreenW / 3 * 2, y: kScreenH / 2 - margin_ViewMidPosition)
        let value_5 = CGPoint.init(x: kScreenW - margin_ViewMidPosition, y: kScreenH / 2 - margin_ViewMidPosition)
        animation_Position.values = [value_0, value_1, value_2, value_3, value_4, value_5]
        
        let animation_BGColor = CABasicAnimation.init(keyPath: "backgroundColor")
        animation_BGColor.toValue = UIColor.green.cgColor
        
        let animation_Rotate = CABasicAnimation.init(keyPath: "transform.rotation")
        animation_Rotate.toValue = NSNumber.init(value: Double.pi * 4)
        
        let animation_Group = CAAnimationGroup()
        animation_Group.animations = [animation_Position, animation_BGColor, animation_Rotate]
        animation_Group.duration = 4.0
        view_Body.layer.add(animation_Group, forKey: "groupAnimation")
 }
  • 连续
 //连续动画 最主要的是处理好各个动画时间的衔接
    func goOnAnimation() {
        
        //定义一个动画开始的时间
        let currentTime = CACurrentMediaTime()
        
        let animation_Position = CABasicAnimation.init(keyPath: "position")
        animation_Position.fromValue = CGPoint.init(x: margin_ViewMidPosition, y: kScreenH / 2)
        animation_Position.toValue = CGPoint.init(x: kScreenW / 2, y: kScreenH / 2)
        animation_Position.duration = 1.0
        animation_Position.fillMode = "forwards" //只在前台
        animation_Position.isRemovedOnCompletion = false //切出界面再回来动画不会停止
        animation_Position.beginTime = currentTime
        view_Body.layer.add(animation_Position, forKey: "positionAnimation")
        
        let animation_Scale = CABasicAnimation.init(keyPath: "transform.scale")
        animation_Scale.fromValue = NSNumber.init(value: 0.7)
        animation_Scale.toValue = NSNumber.init(value: 2.0)
        animation_Scale.duration = 1.0
        animation_Scale.fillMode = "forwards"
        animation_Scale.isRemovedOnCompletion = false
        animation_Scale.beginTime = currentTime + 1.0
        view_Body.layer.add(animation_Scale, forKey: "scaleAnimation")
        
        let animation_Rotate = CABasicAnimation.init(keyPath: "transform.rotation")
        animation_Rotate.toValue = NSNumber.init(value: Double.pi * 4)
        animation_Rotate.duration = 1.0
        animation_Rotate.fillMode = "forwards"
        animation_Rotate.isRemovedOnCompletion = false
        animation_Rotate.beginTime = currentTime + 2.0
        view_Body.layer.add(animation_Rotate, forKey: "rotateAnimation")
    }

4. 过渡动画( CATransition )

CATransitionCAAnimation 的子类,用于做过渡动画或者 转场 动画,能够为层提供移出屏幕和移入屏幕的动画效果。

过渡动画通过 type 设置不同的动画效果, CATransition 有多种过渡效果, 但其实 Apple 官方的SDK只提供了四种:

  • fade 淡出 默认
  • moveIn 覆盖原图
  • push 推出
  • reveal 底部显示出来

私有API提供了其他很多非常炫的过渡动画,如 cube(立方旋转)、suckEffect(吸走)、oglFlip(水平翻转 沿y轴)、 rippleEffect(滴水效果)、pageCurl(卷曲翻页 向上翻页)、pageUnCurl(卷曲翻页 向下翻页)、cameraIrisHollowOpen(相机开启)、cameraIrisHollowClose(相机关闭)等。
注: 因 Apple 不提供维护,并且有可能造成你的app审核不通过, 所以不建议开发者们使用这些私有API.

效果如下:


过渡动画.gif

翻页动画代码如下:

 func curlAnimation() {
        
        let animation_Curl = CATransition()
        animation_Curl.type = "pageCurl"
        animation_Curl.subtype = "fromRight"
        animation_Curl.duration = 1.0
        view_Body.layer.add(animation_Curl, forKey: "curlAnimation")
    }

三、总结

看完整篇文章相信你对 iOS 中的动画有了一个详细的了解, 其实单个动画都是比较简单的, 而复杂的动画其实都是由一个个简单的动画组装而成的,所以遇到比较难得动画需求, 我们只要充分组装不同的动画,就能实现出满意的效果.

你可能感兴趣的:(iOS动画(CoreAnimation))