CoreAnimation
Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。Core Animation可以用在Mac OS X和iOS平台,Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。要注意的是,Core Animation是直接作用在CALayer上的,并非UIView
使用Core Animation创建动画不仅简单,而且具有更好的性能,原因有2个:
- Core Animation动画在单独的线程中完成,不会阻塞主线程
- Core Animation动画只会重绘界面变化的部分(局部刷新)
CAAnimation
所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,不具备动画效果,应该使用它具体的子类.
- CAAnimation
open class CAAnimation : NSObject, NSSecureCoding, NSCopying, CAMediaTiming, CAAction {
open class func defaultValue(forKey key: String) -> Any?
// 返回指定的属性值是否可以归档
open func shouldArchiveValue(forKey key: String) -> Bool
open var timingFunction: CAMediaTimingFunction?
open var delegate: CAAnimationDelegate?
open var isRemovedOnCompletion: Bool
}
- 速度控制函数(CAMediaTimingFunction)
public let kCAMediaTimingFunctionLinear: String //(线性):匀速,给你一个相对静态的感觉
public let kCAMediaTimingFunctionEaseIn: String //(渐进):动画缓慢进入,然后加速离开
public let kCAMediaTimingFunctionEaseOut: String //(渐出):动画全速进入,然后减速的到达目的地
public let kCAMediaTimingFunctionEaseInEaseOut: String // (渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
public let kCAMediaTimingFunctionDefault: String
- CAAnimationDelegate
监听动画的开始和完成
public protocol CAAnimationDelegate : NSObjectProtocol {
optional public func animationDidStart(_ anim: CAAnimation) // 动画开始时调用
optional public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) // 动画结束后调用
}
- isRemovedOnCompletion
默认为true,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为false,不过还要设置fillMode为kCAFillModeForwards.
- CAMediaTiming
public protocol CAMediaTiming {
/* The begin time of the object, in relation to its parent object, if
* applicable. Defaults to 0. */
public var beginTime: CFTimeInterval { get set }
// 动画持续的时间
public var duration: CFTimeInterval { get set }
/* The rate of the layer. Used to scale parent time to local time, e.g.
* if rate is 2, local time progresses twice as fast as parent time.
* Defaults to 1. */
public var speed: Float { get set }
/* Additional offset in active local time. i.e. to convert from parent
* time tp to active local time t: t = (tp - begin) * speed + offset.
* One use of this is to "pause" a layer by setting `speed' to zero and
* `offset' to a suitable value. Defaults to 0. */
public var timeOffset: CFTimeInterval { get set }
// 动画的次数
public var repeatCount: Float { get set }
/* The repeat duration of the object. Defaults to 0. */
public var repeatDuration: CFTimeInterval { get set }
/* When true, the object plays backwards after playing forwards. Defaults
* to NO. */
public var autoreverses: Bool { get set }
/* Defines how the timed object behaves outside its active duration.
* Local time may be clamped to either end of the active duration, or
* the element may be removed from the presentation. The legal values
* are `backwards', `forwards', `both' and `removed'. Defaults to
* `removed'. */
public var fillMode: CAMediaTimingFillMode { get set }
}
- 填充模式(fillMode)
extension CAMediaTimingFillMode {
// 当动画结束后,layer会一直保持着动画最后的状态
public static let forwards: CAMediaTimingFillMode
// 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
public static let backwards: CAMediaTimingFillMode
// 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.
public static let both: CAMediaTimingFillMode
// 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
public static let removed: CAMediaTimingFillMode
}
CAPropertyAnimation
CAPropertyAnimation它是CAAnimation的一个子类,它代表属性动画。当需要使用属性动画时应该使用两个子类CABasicAnimation、CAKeyframeAnimation。
open class CAPropertyAnimation : CAAnimation {
// 便捷的初始化方法
public convenience init(keyPath path: String?)
// CALayer的动画属性名
open var keyPath: String?
// 该属性指定该属性动画是否以当前动画效果为基础
open var isAdditive: Bool
// 该属性指定动画是否为累加效果
open var isCumulative: Bool
//该属性值是一个CAValueFunction对象,该对象负责对属性改变的插值计算,系统已经提供了默认的插值计算方式,因此一般无须指定该属性
open var valueFunction: CAValueFunction?
}
CABasicAnimtion
CABasicAnimation为layer的属性提供了基本的单一关键帧动画。创建CABasicAnimation对象,通过使用父类提供的便捷构造方法public convenienceinit(keyPath path: String?)
,只需要指定能够在渲染树中进行动画的属性keyPath
。
open class CABasicAnimation : CAPropertyAnimation {
open var fromValue: Any? // keyPath相应属性的初始值
open var toValue: Any? // keyPath相应属性的结束值
open var byValue: Any? //相应属性的相对插值, 增加多少值
}
如:创建位置移动基本动画对象:
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = [0, 0]
animation.toValue = [100, 100]
keyPath的值可以为
anchorPoint
backgroundColor
backgroundFilters
borderColor
borderWidth
bounds
compositingFilter
contents
contentsRect
cornerRadius
doubleSided
filters
frame
hidden
mask
masksToBounds
opacity
position
shadowColor
shadowOffset
shadowOpacity
shadowPath
shadowRadius
sublayers
sublayerTransform
transform
zPosition
详细内容可以参考这里 Appendix B、 Core Animation Programming Guide,、 Appendix C
CABasicAnimation可以看做是一种CAKeyframeAnimation的简单动画,因为它只有头尾的关键帧(keyframe)。当设置了CABasicAnimation的起点与终点值后,中间的值都是通过插值方式计算出来的,插值计算是通过timingFunction来指定,timingFunction默认为空,使用liner(匀速运动)。
例如,当我们设置了一个position的动画,设置了开始值PointA与结束值PointB,它们的运动先计算PointA与PointB的中间运动值PointCenter,而PointCenter是由timingFunction来指定值的,并且动画默认是直线匀速运动的。
使用流程
1、创建CALayer图层
2、初始化一个CABasicAnimation对象,给对象设置相关的属性
3、将基本动画对象添加到CALayer对象中就可以开始动画了
背景色动画
let animation = CABasicAnimation(keyPath: "backgroundColor")
animation.fromValue = UIColor.red.cgColor
animation.toValue = UIColor.cyan.cgColor
animation.duration = 1.0
layer.add(animation, forKey: nil)
实现很简单,但是这里会发现一个有趣的现象,即动画结束之后,layer的颜色又回到了最初的状态。如下
为什么动画结束后返回原状态?
首先需要搞明白一点,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。
如果想防止回到最初状态也简单
animation.isRemovedOnCompletion = false
animation.fillMode = kCAFillModeForwards
kCAFillModeForwards该值表示动画即使之后layer的状态将保持在动画的最后一帧,而isRemovedOnCompletion的默认属性值是 true,所以为了使动画结束之后layer保持结束状态,应将isRemovedOnCompletion设置为false。
但是要真正实现layer的最终状态,那么需要修改layer对应的属性。使用上面的属性设置仅仅只是保证了动画结束显示当前状态,但是layer本身并为发生改变。
停止和恢复动画
func pauseLayer(_ layer: CALayer) {
let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
layer.speed = 0.0
layer.timeOffset = pausedTime;
}
func resumeLayer(_ layer: CALayer) {
let pausedTime = layer.timeOffset
layer.speed = 1.0
layer.timeOffset = 0.0
layer.beginTime = 0.0
let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
layer.beginTime = timeSincePause
}
去除正在执行的动画
正常情况下,显示动画会从开始执行一直到完成,但是我们可以根据需求停止正在执行的动画。
// 去除layer上所有的动画
open func removeAllAnimations()
// 去除指定key值的动画
open func removeAnimation(forKey key: String)
实战
移动动画
func positionAnimation() {
let animation = CABasicAnimation(keyPath: "position")
// 对CGPoint进行包装
animation.fromValue = NSValue(cgPoint: layer.position)
animation.toValue = NSValue(cgPoint: CGPoint(x: 300, y: 300))
animation.duration = 1.0
animation.isRemovedOnCompletion = false
animation.fillMode = kCAFillModeForwards
// 动画的次数
animation.repeatCount = 5
layer.add(animation, forKey: nil)
}
效果如下
缩放动画
- 使用bounds进行缩放
func scaleAnimation() {
let animation = CABasicAnimation(keyPath: "bounds.size")
animation.fromValue = NSValue(cgSize: layer.bounds.size)
animation.toValue = NSValue(cgSize: CGSize(width: 200, height: 200))
animation.duration = 1.0
animation.repeatCount = Float.infinity // 无限次执行动画
layer.add(animation, forKey: nil)
}
- 使用transfrom进行缩放
func transformsScaleAnimation() {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.toValue = 2 // 缩放倍率
animation.duration = 1.0
animation.repeatCount = Float.infinity
layer.add(animation, forKey: nil)
}
效果如下
圆角动画
func cornerRadiusAnimation() {
let animation = CABasicAnimation(keyPath: "cornerRadius")
animation.toValue = 50
animation.duration = 2.0
animation.repeatCount = Float.infinity
layer.add(animation, forKey: nil)
}
效果如下
边框动画
func borderAnimation() {
let animation = CABasicAnimation(keyPath: "borderWidth")
animation.toValue = 10
animation.duration = 1
animation.repeatCount = Float.infinity
layer.add(animation, forKey: nil)
}
效果如下
旋转动画
func rotationAnimation() {
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.toValue = CGFloat.pi * 2
animation.duration = 2.0
animation.repeatCount = Float.infinity
// 设置动画速度
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
layer.add(animation, forKey: nil)
}
效果如下
晃动动画
当输入内容错误时,有些app使用了晃动效果提示用户错误,对于UIView而言看这里UIView shake animation
func shakeAnimation() {
let midX = layer.position.x
let midY = layer.position.y
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.06
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = CGPoint(x: midX - 10, y: midY)
animation.toValue = CGPoint(x: midX + 10, y: midY)
layer.add(animation, forKey: nil)
}
效果如下
CAKeyframeAnimation
CAKeyframeAnimation是CAPropertyAnimation的子类,支持关键帧的属性动画,该动画最大的特点在于可通过values属性设置多个关键帧,通过多个关键帧可以指定动画的各个阶段的关键值。
属性
- 关键帧值(Providing keyframe values)
// 关键帧值
open var values: [Any]?
// 路径
open var path: CGPath?
values
是关键帧动画最重要的一部分,该值定义了动画执行的行为,它是一个数组,里面的元素理解为”关键帧"(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一关键帧。
在添加到数组之前,需要注意:有些对象可以直接添加到数组当中,但是有些对象必须在被添加之前需要进行包装,比如:有些对象需要包装为id类型,而标量数据类型或者结构体必须被包装成为对象,例如:
1:如果属性是CGRect
类型(比如: bounds and frame 属性),应该包装每一个矩形成为NSValue
对象。
2:对于layer
的transform
属性,需要包装每一个值CATransform3D
成为NSValue
对象。
3:对于borderColor
属性,在被添加到数组之前,需要CGColorRef
数据类型
4:对于CGFloat
值,包装为NSNumber
对象。
5:对于layer
的contents
属性,使用CGImageRef
属性类型。
6:对于CGPoint
数据类型,可以使用NSValue
对象进行包装,也可以使用CGPathRef
对象使用路径进行包装。
CGPath
这是一个可选的路径对象,默认是nil。它定义了动画的行为,当path的值非nil时,将覆盖values属性的值,作用与values属性一样(即:如果你设置了path,那么values将被忽略。)对于常速路径动画, calculationMode应该被设置为 paced。我们可以设置一个CGPathRef\CGMutablePathRef,让层按照这个路径进行动画移动。
- 关键帧时间(Keyframe timing)
/* An optional array of `NSNumber' objects defining the pacing of the
* animation. Each time corresponds to one value in the `values' array,
* and defines when the value should be used in the animation function.
* Each value in the array is a floating point number in the range
* [0,1]. */
open var keyTimes: [NSNumber]?
/* An optional array of CAMediaTimingFunction objects. If the `values' array
* defines n keyframes, there should be n-1 objects in the
* `timingFunctions' array. Each function describes the pacing of one
* keyframe to keyframe segment. */
open var timingFunctions: [CAMediaTimingFunction]?
/* The "calculation mode". Possible values are `discrete', `linear',
* `paced', `cubic' and `cubicPaced'. Defaults to `linear'. When set to
* `paced' or `cubicPaced' the `keyTimes' and `timingFunctions'
* properties of the animation are ignored and calculated implicitly. */
open var calculationMode: CAAnimationCalculationMode
keyTimes
这是一个可选的轨迹动画的时间数组,数组中的每一个值都是NSNumber对象,并且取值范围在 [0,1]。它定义了动画的步调,数组中的每一个值都与 values中的值一一对应(可以理解为对应的关键帧指定对应的时间点,keyTimes中的每一个时间值都对应values中的每一帧.)。
当keyTimes没有设置的时候,各个关键帧的时间是平分的。默认情况下,一帧动画的播放,分割的时间是动画的总时间除以帧数减去一。
可以通过下面的公式决定每帧动画的时间:总时间/(总帧数-1)
。例如,如果你指定了一个5帧,10秒的动画,那么每帧的时间就是2.5秒钟:10/(5-1)=2.5。你可以做更多的控制通过使用 keyTimes 关键字,你可以给每帧动画指定总时间之内的某个时间点。
例子: [0,0.2,0.5,1];这里面设置的三个的动画时间,假设总时间是10秒,第一段的时间为2秒(0.2 - 0)x10,第二段的时间为3秒(0.5-0.2)x10,第三段的时间为5秒(1-0.5)x10。
timingFunctions
这是一个可选数组,数组中的值是CAMediaTimingFunction类型,如果values数组定义了n关键帧,那么该数组就需要 n-1个CAMediaTimingFunction值。每一个CAMediaTimingFunction值描述了关键帧从一个值到另一个值之间过渡的步调(即:运动的时间函数),具体值参考文章最开始部分。
calculationMode 计算动画的时间
该属性是关键帧动画中还有一个非常重要的参数,所谓计算模式:其主要针对的是每一帧的内容为一个坐标点的情况,也就是对anchorPoint和 position进行的动画。当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算
calculationMode目前提供如下几种模式:
kCAAnimationLinear
默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算,该模式提供了最大化控制动画的时间kCAAnimationDiscrete
离散的,不进行插值计算,所有关键帧直接逐个进行显示,该模式使用keyTimess属性,但是忽略timingFunctions属性。kCAAnimationPaced
使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效kCAAnimationCubic
对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑kCAAnimationCubicPaced
看名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的
注意:如果你想自己处理动画的时间,那么可以使用kCAAnimationLinear or kCAAnimationCubic 模式和keyTimes、timingFunctions属性。keyTimes将定义每一帧所对应的时间,每帧时间的中间值由timingFunctions设置对应的值进行控制。如果没有设置,将使用默认值。
- 旋转模式属性(Rotation Mode Attribute)
/* Defines whether or objects animating along paths rotate to match the
* path tangent. Possible values are `auto' and `autoReverse'. Defaults
* to nil. The effect of setting this property to a non-nil value when
* no path object is supplied is undefined. `autoReverse' rotates to
* match the tangent plus 180 degrees. */
open var rotationMode: CAAnimationRotationMode?
定义是否沿着路径旋转匹配对象动画路径切线,值可能为kCAAnimationRotateAuto和kCAAnimationRotateAutoReverse.默认是nil.如果没有路径对象,设置该属性值将无效,kCAAnimationRotateAutoReverse为了匹配正切将添加180°.
- 立方模式属性(Cubic Mode Attributes)
/* For animations with the cubic calculation modes, these properties
* provide control over the interpolation scheme. Each keyframe may
* have a tension, continuity and bias value associated with it, each
* in the range [-1, 1] (this defines a Kochanek-Bartels spline, see
* http://en.wikipedia.org/wiki/Kochanek-Bartels_spline).
*
* The tension value controls the "tightness" of the curve (positive
* values are tighter, negative values are rounder). The continuity
* value controls how segments are joined (positive values give sharp
* corners, negative values give inverted corners). The bias value
* defines where the curve occurs (positive values move the curve before
* the control point, negative values move it after the control point).
*
* The first value in each array defines the behavior of the tangent to
* the first control point, the second value controls the second
* point's tangents, and so on. Any unspecified values default to zero
* (giving a Catmull-Rom spline if all are unspecified). */
// 该值控制着曲线的紧密度(正值将越紧,负值将越宽松)
open var tensionValues: [NSNumber]?
// 该值控制片段之间的链接(正值将有锋利的圆角,负值将是倒立的圆角)
open var continuityValues: [NSNumber]?
// 该值定义了曲线发生的地点(正值将在在控制点前移动曲线,负值将在控制点后移动)
open var biasValues: [NSNumber]?
实战
背景颜色动画
func colorAnimation() {
let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
animation.values = [UIColor.red.cgColor,
UIColor.green.cgColor,
UIColor.blue.cgColor]
animation.keyTimes = [0, 0.5, 1]
animation.duration = 2
animation.repeatCount = 3
layer.add(animation, forKey: nil)
}
效果如下
晃动动画
func shakeAnimation() {
let animation = CAKeyframeAnimation()
animation.keyPath = "position.x"
animation.values = [0, 10, -10, 10, -5, 5, -5, 0 ]
animation.keyTimes = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]
animation.duration = 1
animation.isAdditive = true
layer.add(animation, forKey: "shake")
}
效果如下
路径动画
沿椭圆轨迹进行动画
func pathAnimation() {
let animation = CAKeyframeAnimation(keyPath: "position")
animation.duration = 2
// 设置动画轨迹,可以根据需求绘制自己需要的动画轨迹
animation.path = UIBezierPath(ovalIn: CGRect(x: 50, y: 150, width: 300, height: 300)).cgPath
animation.repeatCount = Float.infinity
layer.add(animation, forKey: nil)
}
效果如下
CAAnimationGroup
CAAnimationGroup是个动画组,可以同时进行缩放,旋转(同时进行多个动画)。默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。
open class CAAnimationGroup : CAAnimation {
/* An array of CAAnimation objects. Each member of the array will run
* concurrently in the time space of the parent animation using the
* normal rules. */
open var animations: [CAAnimation]?
}
实战
透明度和背景色的组合效果
func groupAnimation() {
// 透明度
let fadeOut = CABasicAnimation(keyPath: "opacity")
fadeOut.fromValue = 1
fadeOut.toValue = 0.1
fadeOut.duration = 1
// 缩放
let expandScale = CABasicAnimation()
expandScale.keyPath = "transform"
expandScale.valueFunction = CAValueFunction(name: kCAValueFunctionScale)
expandScale.fromValue = [1, 1, 1]
expandScale.toValue = [3, 3, 3]
// 组合动画
let fadeAndScale = CAAnimationGroup()
fadeAndScale.animations = [fadeOut, expandScale]
fadeAndScale.duration = 1
fadeAndScale.repeatCount = 5
fadeAndScale.autoreverses = true
layer.add(fadeAndScale, forKey: nil)
}
效果如下
注意
The
delegate
andisRemovedOnCompletion
properties of animations in theanimations
array are currently ignored. TheCAAnimationGroup
delegate does receive these messages.
CATransition
CATransitions是CAAnimation的子类,CATransition类为layer实现了过渡动画,我们可以从一系列预定义的过渡动画中指定过渡效果或者提供自定义的CIFilter实例。
open class CATransition : CAAnimation {
open var type: CATransitionType //动画过渡类型
open var subtype: CATransitionSubtype? //动画过渡类型
open var startProgress: Float //动画起点(在整体动画的百分比)
open var endProgress: Float //动画起点(在整体动画的百分比)
}
type
extension CATransitionType {
// 交叉淡化过渡
public static let fade: CATransitionType
// 交叉淡化过渡
public static let moveIn: CATransitionType
// 新视图把旧视图推出去
public static let push: CATransitionType
// 将旧视图移开,显示下面的新视图
public static let reveal: CATransitionType
}
subtype
extension CATransitionSubtype {
// 指定动画方向,从右到左
public static let fromRight: CATransitionSubtype
// 指定动画方向,从左向右
public static let fromLeft: CATransitionSubtype
// 指定动画方向,从左向右
public static let fromTop: CATransitionSubtype
// 指定动画方向,从下到上
public static let fromBottom: CATransitionSubtype
}
CATransition通常用于通过CALayer控制UIView内子控件的过渡动画,比如,删除子控件,添加子控件,切换两个子控件等.具体使用步骤总结:
- 1:创建
CATransition
对象. - 2:为
CATransition
设置type
和subtype
两个属性,其中 type 指定动画的类型,subtype指定动画移动的方向. - 3:如果不需要动画执行的整个过程(就是只要动画执行到中间部分就停止),可以指定
startProgress
(动画开始的进度),endProgress
(动画结束的进度)属性. - 4:调用layer的
init(keyPath path: String?)
即可进行动画。第一个参数为CAAnimation对象,第二个参数为用于该动画对象执行的唯一标识。
实战
写一个简单的demo,点击屏幕切换视图
class CATrainsitionViewController: UIViewController {
lazy var cyanView: UIView = {
let view = UIView(frame: self.view.bounds)
view.backgroundColor = UIColor.cyan
return view
}()
lazy var redView: UIView = {
let view = UIView(frame: self.view.bounds)
view.backgroundColor = UIColor.red
return view
}()
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(cyanView)
view.addSubview(redView)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let toView = count % 2 == 0 ? cyanView: redView
count += 1
// animation here...
view.bringSubview(toFront: toView)
}
}
moveIn
let transition = CATransition()
transition.duration = 2.0
transition.type = kCATransitionMoveIn
transition.subtype = kCATransitionFromRight
view.layer.add(transition, forKey: nil)
下面几种效果修改type即可,subtype根据需求设置方向
fade
transition.type = kCATransitionFade
transition.subtype = kCATransitionFromTop
reveal
transition.type = kCATransitionReveal
transition.subtype = kCATransitionFromTop
push
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromLeft
CASpringAnimation
CASpringAnimation iOS9才引入的动画类,它继承于CABaseAnimation,用于制作弹簧动画效果。
/** Subclass for mass-spring animations. */
@available(iOS 9.0, *)
open class CASpringAnimation : CABasicAnimation {
// 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大,动画的速度变慢.默认值为1
open var mass: CGFloat
// 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快。默认值为100
open var stiffness: CGFloat
// 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快。默认值为10
open var damping: CGFloat
// 初始速率,动画视图的初始速度大小,速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
open var initialVelocity: CGFloat
// 结算时间返回弹簧动画到停止时的估算时间,根据当前的动画参数估算,通常弹簧动画的时间使用结算时间比较准确
open var settlingDuration: CFTimeInterval { get }
}
实战
实现触摸屏幕,使红色小方块移动到对应的点,并且来回上下弹性运动
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let touchPoint = touch.location(in: self.view)
let animation = CASpringAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: layer.position)
animation.toValue = NSValue(cgPoint: touchPoint)
animation.damping = 5 //阻尼系数越大,停止越快
animation.stiffness = 100 //刚度系数越大,形变产生的力就越大,运动越快
animation.mass = 1 //速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
animation.duration = 2 //质量越大,弹簧拉伸和压缩的幅度越大
layer.add(animation, forKey: nil)
layer.position = touchPoint
}
效果如下