1 Core Animation介绍
CoreAnimation翻译过来就是核心动画,一组非常强大的API,用来做动画的,非常的简单但是效果非常绚丽。
- CoreAnimation是跨平台的,既可以支持IOS,也支持MAC OS
- CoreAnimation执行动画是在后台,不会阻塞主线程
- CoreAnimation作用在CALayer,不是UIView
- 通过调用CALayer的addAnimation:forKey:方法增加CAAnimation对象到CALayer中,这样就能开始执行动画了
- 通过调用CALayer的removeAnimationForKey:方法可以停止CALayer中的动画
2 CoreAnimation及其相关属性
CoreAnimation类的继承关系图如下,其中CAMediaTiming是一个协议(protocol)
注意:
- CAAnimation是所有动画类的父类,但是它不能直接使用,应该使用它的子类
- CAPropertyAnimation也是不能直接使用的,也要使用它的子类
能用的动画类只剩下4个:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup
2.1 常用属性
- removedOnCompletion:默认为true,代表动画执行完毕后就从图层上移除 ,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为false。
2.timingFunction:控制动画运行的节奏。
/** timingFunction可选的值 **/
@available(iOS 2.0, *)
public let kCAMediaTimingFunctionLinear: String
//1.(匀速): 在整个动画时间内动画都是以一个相同的速度来改变
@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseIn: String
//2. (渐进): 缓慢进入, 加速离开
@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseOut: String
//3. (渐出): 快速进入, 减速离开
@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseInEaseOut: String
//4. (渐进渐出): 缓慢进入, 中间加速, 减速离开
@available(iOS 3.0, *)
public let kCAMediaTimingFunctionDefault: String
//5. (默认): 效果基本等同于EaseOut(渐出)
- fillMode:决定当前对象在非active时间段的行为。
- 要想fillMode有效,需设置removedOnCompletion = false
- fillMode可选的值
/* `fillMode' options. */
@available(iOS 2.0, *)
public let kCAFillModeForwards: String
//1. 当动画结束后,layer会一直保持着动画最后的状态
@available(iOS 2.0, *)
public let kCAFillModeBackwards: String
//2. 设置为该值,将会立即执行动画的第一帧,不论是否设置了 beginTime属性。观察发现,设置该值,刚开始视图不见,还不知道应用在哪里
@available(iOS 2.0, *)
public let kCAFillModeBoth: String
//3. 该值是 kCAFillModeForwards 和 kCAFillModeBackwards的组合状态; 动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
@available(iOS 2.0, *)
public let kCAFillModeRemoved: String
//4. 默认值,动画将在设置的 beginTime 开始执行(如没有设置beginTime属性,则动画立即执行),
- delegate:动画代理,用来监听动画的执行过程。
public protocol CAAnimationDelegate : NSObjectProtocol {
// 动画开始执行的时候触发这个方法
@available(iOS 2.0, *)
optional public func animationDidStart(_ anim: CAAnimation)
// 动画执行完毕的时候触发这个方法
@available(iOS 2.0, *)
optional public func animationDidStop(_ anim: CAAnimation, finished flag: Bool)
}
- 其他相关属性
duration 动画的时长
repeatCount 重复的次数。不停重复设置为 HUGE_VALF
repeatDuration 设置动画的时间。在该时间内动画一直执行,不计次数。
beginTime 指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的方式
timingFunction 设置动画的速度变化
autoreverses 动画结束时是否执行逆动画
fromValue 所改变属性的起始值(Swift中为Any类型,OC中要包装成NSValue对象)
toValue 所改变属性的结束时的值(类型与fromValue相同)
byValue 所改变属性相同起始值的改变量(类型与fromValue相同)
2.2 animationWithKeyPath可以使用的keyPath
animationWithKeyPath苹果官方介绍传送门
anchorPoint
backgroundColor
backgroundFilters
borderColor
borderWidth
bounds
compositingFilter
contents
contentsRect
cornerRadius
doubleSided
filters
frame :This property is not animatable. You can achieve the same results by animating theboundsandpositionproperties.
hidden
mask
masksToBounds
opacity
position
shadowColor
shadowOffset
shadowOpacity
shadowPath
shadowRadius
sublayers
sublayerTransform
transform 翻转包含scale rotation
zPosition
//大部分我们常用的是:
transform.scale = 比例缩放动画
transform.scale.x = 宽的比例动画
transform.scale.y = 高的比例动画
transform.rotation.z = 平面的旋转
opacity = 透明度
3 动画实例
上文讲到可用的四个动画类:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup。iOS9之后还有CASpringAnimation。先分别针对每个动画类写实例以进行加深。
3.1 CABasicAnimation
let redLabel = UILabel()
redLabel.frame = CGRect(x: 20, y: 20, width: 100, height: 50)
redLabel.backgroundColor = UIColor.red
self.view.addSubview(redLabel)
let caBasic = CABasicAnimation(keyPath: "position")
caBasic.duration = 2
caBasic.fromValue = redLabel.layer.position
caBasic.toValue = CGPoint(x: 50, y: 200)
caBasic.isRemovedOnCompletion = false
caBasic.delegate = self
caBasic.fillMode = CAMediaTimingFillMode.forwards
redLabel.layer.add(caBasic, forKey: "redLabel1")
//开始执行
func animationDidStart(_ anim: CAAnimation) {
print("开始动画--layer:", redLabel.layer.position)
}
//结束之行
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("结束动画--layer:", redLabel.layer.position)
}
3.2 CAKeyframeAnimation
关键帧动画,也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:
- CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue)
- 而CAKeyframeAnimation会使用一个Array保存这些数值
- CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation
关键属性说明:
//1.上述的Array对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
open var values: [Any]?
//2.可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略
open var path: CGPath?
//3. 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的
open var keyTimes: [NSNumber]?
//4. 用它来对每次动画的步骤指定不同的计时函数。但是指定函数的个数一定要等 于 keyframes 数组的元素个数减一,因为它是描述每一帧之间动画速度的函数。
open var timingFunctions: [CAMediaTimingFunction]?
//5.该属性决定了物体在每个子路径下是跳着走还是匀速走,跟timeFunctions属性有点类似
//kCAMediaTimingFunctionLinear 传这个值,在整个动画时间内动画都是以一个相同的速度来改变。也就是匀速运动。
//kCAMediaTimingFunctionEaseIn 使用该值,动画开始时会较慢,之后动画会加速。
//kCAMediaTimingFunctionEaseOut 使用该值,动画在开始时会较快,之后动画速度减慢。
//kCAMediaTimingFunctionEaseInEaseOut 使用该值,动画在开始和结束时速度较慢,中间时间段内速度较快。
open var calculationMode: CAAnimationCalculationMode
//6.定义曲线紧密度的NSNumber对象数组
open var tensionValues: [NSNumber]?
//7.NSNumber对象的数组,定义了时序曲线拐角的锐度
open var continuityValues: [NSNumber]?
//8.一个NSNumber对象数组,用于定义曲线相对于控制点的位置
open var biasValues: [NSNumber]?
//9.确定沿路径动画的对象是否旋转以匹配路径切线
open var rotationMode: CAAnimationRotationMode?
实例:
let key = CAKeyframeAnimation(keyPath: "position")
key.duration = 3
key.repeatCount = HUGE //无线循环
key.calculationMode = CAAnimationCalculationMode.paced
key.values = [redLabel.frame.origin, CGPoint(x: 180, y: 70), CGPoint(x: 180, y: 200), redLabel.frame.origin]
key.keyTimes = [NSNumber(value: 0.0), NSNumber(value: 0.6), NSNumber(value: 0.7), NSNumber(value: 0.8)]
redLabel.layer.add(key, forKey: "key")
3.3 CATransition
- CATransition是CAAnimation的子类,用于做转场动画,能够为layer层提供移出屏幕和移入屏幕的动画效果。
- iOS比Mac OS X的转场动画效果少一点,UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果
动画属性:
- type:动画过渡类型
- subtype:动画过渡方向
- startProgress:动画起点(在整体动画的百分比)
- endProgress:动画终点(在整体动画的百分比)
实例:
//0.初始化ImageView
imageView.isUserInteractionEnabled = true
imageView.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
imageView.image = UIImage(named: "0.jpg")
self.view.addSubview(imageView)
//1. 添加滑动手势
let left = UISwipeGestureRecognizer(target: self, action: #selector(leftSwipe(gesture:)))
left.direction = .left
imageView.addGestureRecognizer(left)
let right = UISwipeGestureRecognizer(target: self, action: #selector(rightSwipe(gesture:)))
right.direction = .right
imageView.addGestureRecognizer(right)
//MARK: 手势相关方法
//左滑
@objc fileprivate func leftSwipe(gesture: UIGestureRecognizer) {
print("左滑动")
transitionAnimation(isNext: true)
}
//右滑
@objc fileprivate func rightSwipe(gesture: UIGestureRecognizer) {
print("右滑动")
transitionAnimation(isNext: false)
}
//设置转场动画
fileprivate func transitionAnimation(isNext: Bool){
let transition = CATransition()
transition.type = CATransitionType.fade
transition.subtype = isNext ? CATransitionSubtype.fromRight : CATransitionSubtype.fromLeft
transition.duration = 1
imageView.image = getImage(isNext)
imageView.layer.add(transition, forKey: "transition")
}
//获取下/上一张图片
fileprivate func getImage(_ isNext: Bool) -> UIImage {
currentIndex = isNext ? currentIndex + 1 : currentIndex - 1
currentIndex = currentIndex < 0 ? 3 : currentIndex
currentIndex = currentIndex > 3 ? 0 : currentIndex
return UIImage(named: "\(currentIndex)" + ".jpg")!
}
3.4 CAAnimationGroup
- 是CAAnimation的子类,可以保存一组动画对象
- 将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行
属性说明:
- animations:用来保存一组动画对象的Array
- 默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间
实例:
let caBasic = CABasicAnimation(keyPath: "position")
caBasic.duration = 2
caBasic.fromValue = redLabel.layer.position
caBasic.toValue = CGPoint(x: 50, y: 200)
caBasic.isRemovedOnCompletion = false
caBasic.delegate = self
caBasic.fillMode = CAMediaTimingFillMode.forwards
let caBasic1 = CABasicAnimation(keyPath: "transform.scale")
caBasic1.duration = 2
caBasic1.toValue = CATransform3DMakeScale(2, 1.5, 1)
caBasic1.isRemovedOnCompletion = false
caBasic1.delegate = self
let aniGroup = CAAnimationGroup();
aniGroup.duration = 2
aniGroup.animations = [caBasic,caBasic1];
redLabel.layer.add(aniGroup, forKey: "redLabel1")
3.5 CASpringAnimation
- CASpringAnimation是iOS 9 新出的
- CASpringAnimation 继承于CABaseAnimation
- CASpringAnimation是苹果专门解决开发者关于弹簧动画的这个需求而封装的类
相关属性:
//1. 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大, 默认值: 1
open var mass: CGFloat
//2. 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快(默认值: 100)
open var stiffness: CGFloat
//3. 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快(默认值: 10)
open var damping: CGFloat
//4. 初始速率,动画视图的初始速度大小, 默认0
//速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反(默认值: 0)
open var initialVelocity: CGFloat
//5. 估算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算(只读)
open var settlingDuration: CFTimeInterval { get }
实例:
let spring = CASpringAnimation(keyPath: "position.y")
spring.mass = 5
spring.stiffness = 100
spring.damping = 5
spring.initialVelocity = 2
spring.fromValue = redLabel.layer.position.y
spring.toValue = redLabel.layer.position.y + 250
spring.duration = spring.settlingDuration
redLabel.layer.add(spring, forKey: "spring")
4 总结
- CoreAnimation给我们展示的只是一个假象,layer的的frame、bounds、position并不会在动画完毕之后发生改变。(这个后面会出一片View和Layer的文章来补充说明这个问题)
- UIView封装的动画,会使会真实修改view的一些属性
参考文章:
iOS CAAnimation核心动画
iOS动画之CAKeyframeAnimation
iOS出门必备之CoreAnimation(核心动画)