iOS动画CoreAnimation总结

1 Core Animation介绍

CoreAnimation翻译过来就是核心动画,一组非常强大的API,用来做动画的,非常的简单但是效果非常绚丽。

  1. CoreAnimation是跨平台的,既可以支持IOS,也支持MAC OS
  2. CoreAnimation执行动画是在后台,不会阻塞主线程
  3. CoreAnimation作用在CALayer,不是UIView
  4. 通过调用CALayer的addAnimation:forKey:方法增加CAAnimation对象到CALayer中,这样就能开始执行动画了
  5. 通过调用CALayer的removeAnimationForKey:方法可以停止CALayer中的动画

2 CoreAnimation及其相关属性

CoreAnimation类的继承关系图如下,其中CAMediaTiming是一个协议(protocol)


CoreAnimation类的继承关系图

注意

  1. CAAnimation是所有动画类的父类,但是它不能直接使用,应该使用它的子类
  2. CAPropertyAnimation也是不能直接使用的,也要使用它的子类

能用的动画类只剩下4个:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup

2.1 常用属性

  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(渐出)
  1. 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属性,则动画立即执行),
  1. 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)
}
  1. 其他相关属性
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(核心动画)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容