前段时间接触到了一个牛逼的动画框架POP ,本来想来装装逼,突然发现,苹果大大的CoreAnimation我还不会用呢!
依稀记得乔帮主在2007年的WWDC大会上亲自为你演示Core Animation的强大:点击查看视频 (不好意思,又装逼了)
言归正传,我只是来温习一下CoreAnimation,还望路过的大神不要吐槽我太low
GitHub项目地址
Core Animation简介
Core Animation
,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。
Core Animation
可以用在Mac OS X和iOS
平台。
Core Animation
的动画执行过程都是在后台操作的,不会阻塞主线程。
要注意的是,Core Animation是直接作用在CALayer
上的,并非UIView
通过调用CALayer
的addAnimation:forKey:
方法增加CAAnimation
对象到CALayer
中,这样就能开始执行动画了
通过调用CALayer
的removeAnimationForKey:
方法可以停止CALayer
中的动画
Core Animation及其相关属性
要想执行动画,就必须初始化一个CAAnimation
对象。
一般情况下,我们使用的比较多的是CAAnimation
的子类,因此,先大致看看CAAnimation
的继承结构
黑线代表继承,黑色文字代表类名,白色文字代表属性。其中CAMediaTiming
是一个协议(protocol
)
CAAnimation是所有动画类的父类,但是它不能直接使用,应该使用它的子类
CAPropertyAnimation也是不能直接使用的,也要使用它的子类
能用的动画类只剩下4个:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup
常用属性 1). removedOnCompletion
:默认为true,代表动画执行完毕后就从图层上移除
图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为false,不过还要设置fillMode
为kCAFillModeForwards
2). timingFunction:控制动画运行的节奏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @available(iOS 2.0 , *) public let kCAMediaTimingFunctionLinear: String @available(iOS 2.0 , *) public let kCAMediaTimingFunctionEaseIn: String @available(iOS 2.0 , *) public let kCAMediaTimingFunctionEaseOut: String @available(iOS 2.0 , *) public let kCAMediaTimingFunctionEaseInEaseOut: String @available(iOS 3.0 , *) public let kCAMediaTimingFunctionDefault: String
3). fillMode决定当前对象在非active时间段的行为。
要想fillMode有效,需设置removedOnCompletion = false
fillMode可选的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @available(iOS 2.0 , *) public let kCAFillModeForwards: String @available(iOS 2.0 , *) public let kCAFillModeBackwards: String @available(iOS 2.0 , *) public let kCAFillModeBoth: String @available(iOS 2.0 , *) public let kCAFillModeRemoved: String
4). delegate
:动画代理,用来监听动画的执行过程
1 2 3 4 5 6 7 8 9 10 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) }
5). 其他相关属性
1 2 3 4 5 6 7 8 9 duration 动画的时长 repeatCount 重复的次数。不停重复设置为 HUGE_VALF repeatDuration 设置动画的时间。在该时间内动画一直执行,不计次数。 beginTime 指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime () + 秒数】 的方式 timingFunction 设置动画的速度变化 autoreverses 动画结束时是否执行逆动画 fromValue 所改变属性的起始值(Swift中为Any类型,OC中要包装成NSValue 对象) toValue 所改变属性的结束时的值(类型与fromValue相同) byValue 所改变属性相同起始值的改变量(类型与fromValue相同)
CABasicAnimation
CABasicAnimation是CAPropertyAnimation的子类,使用它可以实现一些基本的动画效果,它可以让CALayer的某个属性从某个值渐变到另一个值。下面就用CABasicAnimation实现几个简单的动画
平移动画 方法一: 改变label的position 1 2 3 4 5 6 7 8 let caBasic = CABasicAnimation (keyPath: "position" ) caBasic.duration = 2 caBasic.fromValue = redLabel.layer.position caBasic.toValue = CGPoint (x: kScreenWidth - 50 , y: 200 ) caBasic.delegate = self caBasic.isRemovedOnCompletion = false caBasic.fillMode = kCAFillModeForwards redLabel.layer.add(caBasic, forKey: "redLabel1" )
初始化方法中是@”position”,说明要修改的是CALayer的position属性,也就是会执行平移动画
默认情况下,动画执行完毕后,动画会自动从CALayer上移除,CALayer又会回到原来的状态。为了保持动画执行后的状态,可以加入第6、7行代码
第8行后面的@”redLabel1”是给动画对象起个名称,以后可以调用CALayer的removeAnimationForKey:方法根据动画名称停止相应的动画
遵循的代理方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 extension ViewController: CAAnimationDelegate { func animationDidStart(_ anim: CAAnimation ) { print("开始动画--layer:" , redLabel.layer.position) } func animationDidStop(_ anim: CAAnimation , finished flag: Bool) { print("结束动画--layer:" , redLabel.layer.position) } }
从打印信息可以看出,实际上,动画执行完毕后,并没有真正改变CALayer的position属性的值!
方法二. 1 2 3 4 5 let basic = CABasicAnimation (keyPath: "transform" ) basic.duration = 2 let form = CATransform3DMakeTranslation (350 , 400 , 0 ) basic.toValue = form blueLabel.layer.add(basic, forKey: "blueLabel" )
旋转动画 1 2 3 4 5 6 let basic1 = CABasicAnimation (keyPath: "transform" ) basic1.duration = 1 basic1.toValue = CATransform3DMakeRotation (0.25 , 0 , 0 , 1 ) basic1.isRemovedOnCompletion = false basic1.fillMode = kCAFillModeForwards blueLabel.layer.add(basic1, forKey: "basic1" )
可以不用设置fromValue,这里只设置了toValue
缩放动画
CALayer的宽度从0.5倍变为2倍
CALayer的高度从0.5倍变为1.5倍
1 2 3 4 5 6 7 let basic1 = CABasicAnimation (keyPath: "transform" ) basic1.duration = 1 basic1.toValue = CATransform3DMakeScale (0.5 , 0.5 , 1 ) basic1.toValue = CATransform3DMakeScale (2 , 1.5 , 1 ) basic1.isRemovedOnCompletion = false basic1.fillMode = kCAFillModeForwards blueLabel.layer.add(basic1, forKey: "basic1" )
CABasicAnimation虽然能够做很多基本的动画效果,但是有个局限性,只能让CALayer的属性从某个值渐变到另一个值,仅仅是在2个值之间渐变
总结一些常用的animationKeyPath值的
值
说明
使用形式
transform.scale
比例转化
0.5
transform.scale.x
宽的比例
0.5
transform.rotation.x
围绕x轴旋转
@(M_PI_4)(OC), 0.25(Swift)
cornerRadius
圆角的设置
30
backgroundColor
背景颜色的变化
UIColor.purpleColor.cgColor
bounds
大小,中心不变
CGRect
position
位置(中心点的改变)
CGPoint
contents
内容,比如UIImageView的图片
imageAnima.toValue = UIImage(named: “toImage”)?.cgImage
opacity
透明度
0.7
contentsRect.size.width
横向拉伸缩放
最好是0~1之间的
CAKeyframeAnimation——关键帧动画
关键帧动画,也是CAPropertyAnimation
的子类,与CABasicAnimation
的区别是:
CABasicAnimation
只能从一个数值(fromValue)变到另一个数值(toValue)
而CAKeyframeAnimation
会使用一个Array保存这些数值
属性说明:
values
:上述的Array对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path
:可以设置一个CGPathRef、CGMutablePathRef
,让图层按照路径轨迹移动。path只对CALayer的anchorPoint
和position
起作用。如果设置了path,那么values将被忽略
keyTimes
:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的
calculationMode
: 该属性决定了物体在每个子路径下是跳着走还是匀速走,跟timeFunctions
属性有点类似
kCAAnimationLinear
默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;
kCAAnimationDiscrete
离散的,就是不进行插值计算,所有关键帧直接逐个进行显示;
kCAAnimationPaced
使得动画均匀进行,而不是按keyTimes
设置的或者按关键帧平分时间,此时keyTimes
和timingFunctions`无效;
kCAAnimationCubic
对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues
来进行调整自定义主要目的是使得运行的轨迹变得圆滑;
kCAAnimationCubicPaced
看这个名字就知道和kCAAnimationCubic
有一定联系,其实就是在kCAAnimationCubic
的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes
以及timingFunctions
也是无效的.
CABasicAnimation
可看做是只有2个关键帧的CAKeyframeAnimation
values方式 1 2 3 4 5 6 7 let key = CAKeyframeAnimation (keyPath: "position" ) key.duration = 3 key.repeatCount = HUGE key.calculationMode = kCAAnimationPaced 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" )
CASpringAnimation
CASpringAnimation
是iOS 9 新出的
CASpringAnimation
继承于CABaseAnimation
CASpringAnimation
是苹果专门解决开发者关于弹簧动画的这个需求而封装的类。
CASpringAnimation相关属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 open var mass: CGFloat open var stiffness: CGFloat open var damping: CGFloat open var initialVelocity: CGFloat open var settlingDuration: CFTimeInterval { get }
示例代码 1 2 3 4 5 6 7 8 9 let spring = CASpringAnimation (keyPath: "position.y" ) spring.mass = 5 spring.stiffness = 100 spring.damping = 5 spring.initialVelocity = 2 spring.fromValue = blueLabel.layer.position.y spring.toValue = kScreenHeight - 150 spring.duration = spring.settlingDuration blueLabel.layer.add(spring, forKey: "spring" )
CAAnimationGroup动画组
是CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行
属性说明:
animations:用来保存一组动画对象的Array
默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 fileprivate func getCAAnimationGroup(){ let group = CAAnimationGroup () let basic1 = CABasicAnimation (keyPath: "position" ) basic1.fromValue = blueLabel.layer.position basic1.toValue = CGPoint (x: CGFloat (arc4random_uniform(200 )), y: CGFloat (arc4random_uniform(500 ))) let basic2 = CABasicAnimation (keyPath: "transform.scale" ) var scale: CGFloat = 0.1 scale = scale < 1 ? 1.5 : 0.5 basic2.toValue = scale let basic3 = CABasicAnimation (keyPath: "transform.rotation" ) basic3.toValue = CGFloat (arc4random_uniform(360 )) / 180.0 group.animations = [basic1, basic2, basic3] group.isRemovedOnCompletion = false group.fillMode = kCAFillModeForwards group.duration = 0.5 blueLabel.layer.add(group, forKey: "group" ) }
转场动画——CATransition
CATransition
是CAAnimation
的子类,用于做转场动画,能够为layer层提供移出屏幕和移入屏幕的动画效果。
iOS比Mac OS X的转场动画效果少一点UINavigationController
就是通过CATransition
实现了将控制器的视图推入屏幕的动画效果
动画属性:
type
:动画过渡类型
subtype
:动画过渡方向
startProgress
:动画起点(在整体动画的百分比)
endProgress
:动画终点(在整体动画的百分比)
type
和subtype
属性说明1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @available(iOS 2.0 , *) public let kCATransitionFade: String @available(iOS 2.0 , *) public let kCATransitionMoveIn: String @available(iOS 2.0 , *) public let kCATransitionPush: String @available(iOS 2.0 , *) public let kCATransitionReveal: String @available(iOS 2.0 , *) public let kCATransitionFromRight: String @available(iOS 2.0 , *) public let kCATransitionFromLeft: String @available(iOS 2.0 , *) public let kCATransitionFromTop: String @available(iOS 2.0 , *) public let kCATransitionFromBottom: String
除了上述四种效果之外,还有很多私有API效果,使用的时候要小心,可能会导致app审核不被通过
使用的时候要以字符串的形式
1 2 3 4 5 6 7 8 9 cube oglFlip suckEffect rippleEffect pageCurl pageUnCurl cameraIrisHollowOpen cameraIrisHollowClose
效果参考
代码示例:
初始化变量 1 2 3 4 fileprivate var imageView = UIImageView (frame: UIScreen .main.bounds) fileprivate var currentIndex = 0
需要在viewDidLoad
中调用一下方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fileprivate func imageCATransition(){ imageView.isUserInteractionEnabled = true imageView.image = UIImage (named: "0.jpg" ) view.addSubview(imageView) 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) }
滑动后执行的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @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 = kCATransitionFade transition.subtype = isNext ? kCATransitionFromRight : kCATransitionFromLeft 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 ? 7 : currentIndex currentIndex = currentIndex > 7 ? 0 : currentIndex return UIImage (named: "\(currentIndex)" + ".jpg" )! }
总结
核心动画给我们展示的只是一个假象,layer的的frame、bounds、position并不会在动画完毕之后发生改变。
UIView封装的动画,会使会真实修改view的一些属性
以上就是小编总结的关于Core Animation核心动画的相关分类
总结的知识点比较简单, 个人感觉有点low
如有不足之处,还望路过的大神多多指教