一、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: 控制动画的显示节奏, 系统提供五种值选择,分别是:
-
kCAMediaTimingFunctionDefault
( 默认,中间快) -
kCAMediaTimingFunctionLinear
(线性动画) -
kCAMediaTimingFunctionEaseIn
(先慢后快 慢进快出) -
kCAMediaTimingFunctionEaseOut
(先块后慢快进慢出) -
kCAMediaTimingFunctionEaseInEaseOut
(先慢后快再慢)
-
-
type: 过渡动画的动画类型,系统提供了多种过渡动画, 分别是:
-
fade
(淡出 默认) -
moveIn
(覆盖原图) -
push
(推出) -
fade
(淡出 默认) -
reveal
(底部显示出来) -
cube
(立方旋转) -
suck
(吸走) -
oglFlip
(水平翻转 沿y轴) -
ripple
(滴水效果) -
curl
(卷曲翻页 向上翻页) -
unCurl
(卷曲翻页返回 向下翻页) -
caOpen
(相机开启) -
caClose
(相机关闭)
-
-
subtype : 过渡动画的动画方向, 系统提供了四种,分别是:
-
fromLeft
( 从左侧) -
fromRight
(从右侧) -
fromTop
(有上面) -
fromBottom
(从下面)
-
mass: 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
stiffness:弹簧动画的刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
damping:阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
initialVelocity:初始速率,动画视图的初始速度大小
速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反settlingDuration:结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
通常弹簧动画的时间使用结算时间比较准确
二、Core Animation的使用
1. 基础动画(CABasicAnimation
)
基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、旋转、缩放、透明度、背景色等。
基础动画根据keyPath
来区分不同的动画, 系统提供了多个类型,如:
基础动画根据keyPath
来区分不同的动画, 系统提供了多个类型,如:transform.scale
(比例转换)、transform.scale.x
、transform.scale.y
、transform.rotation
(旋转) 、transform.rotation.x
(绕x轴旋转)、transform.rotation.y
(绕y轴旋转)、transform.rotation.z
(绕z轴旋转)、opacity
(透明度)、margin
、backgroundColor
(背景色)、cornerRadius
(圆角)、borderWidth
(边框宽)、bounds
、contents
、contentsRect
、cornerRadius
、frame
、hidden
、mask
、masksToBounds
、shadowColor
(阴影色)、shadowOffset
、shadowOpacity
, 在使用时候, 需要根据具体的需求选择合适的。
- 位移动画:
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
)
CAKeyframeAnimation
和CABasicAnimation
都属于CAPropertyAnimatin
的子类。不同的是CABasicAnimation
只能从一个数值(fromValue
)变换成另一个数值(toValue
),而CAKeyframeAnimation
则会使用一个数组(values
) 保存一组关键帧, 也可以给定一个路径(path
)制作动画。
CAKeyframeAnimation主要有 三个 重要属性:
- values:存放关键帧(
keyframe
)的数组,动画对象会在指定的时间(duration
)内,依次显示values数组中的每一个关键帧 . - path:可以设置一个
CGPathRef
或CGMutablePathRef
,让层跟着路径移动.path
只对 CALayer 的anchorPoint
和position
起作用, 如果设置了path
,那么values将被忽略. - keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,
keyTimes
中的每一个时间值都对应values
中的每一帧.当keyTimes
没有设置的时候,各个关键帧的时间是根据duration
平分的。
以抖动截图为例, 效果图如下:
动画代码如下:
- 关键帧动画
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
,用于制作弹簧动画,先演示个例子
动画代码如下:
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
)
CAAnimationGroup
是 CAAnimation
的子类,可以保存一组动画对象,可以保存基础动画、关键帧动画等,数组中所有动画对象可以同时并发运行, 也可以通过实践设置为串行连续动画.
效果截图如下:
动画代码如下:
- 同时
//同时
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
)
CATransition
是 CAAnimation
的子类,用于做过渡动画或者 转场 动画,能够为层提供移出屏幕和移入屏幕的动画效果。
过渡动画通过 type
设置不同的动画效果, CATransition
有多种过渡效果, 但其实 Apple
官方的SDK只提供了四种:
- fade 淡出 默认
- moveIn 覆盖原图
- push 推出
- reveal 底部显示出来
但私有API提供了其他很多非常炫的过渡动画,如 cube
(立方旋转)、suckEffect
(吸走)、oglFlip
(水平翻转 沿y轴)、 rippleEffect
(滴水效果)、pageCurl
(卷曲翻页 向上翻页)、pageUnCurl
(卷曲翻页 向下翻页)、cameraIrisHollowOpen
(相机开启)、cameraIrisHollowClose
(相机关闭)等。
注: 因 Apple
不提供维护,并且有可能造成你的app审核不通过, 所以不建议开发者们使用这些私有API.
效果如下:
翻页动画代码如下:
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 中的动画有了一个详细的了解, 其实单个动画都是比较简单的, 而复杂的动画其实都是由一个个简单的动画组装而成的,所以遇到比较难得动画需求, 我们只要充分组装不同的动画,就能实现出满意的效果.