Core Animation,中文翻译为核心动画,它是一组很是强大的动画处理API,使用它能作出很是炫丽的动画效果,并且每每是事半功倍。也就是说,使用少许的代码就能够实现很是强大的功能。函数
Core Animation是跨平台的,能够用在Mac OS X和iOS平台。动画
Core Animation的动画执行过程都是在后台操做的,不会阻塞主线程。不阻塞主线程,能够理解为在执行动画的时候还能点击(按钮)。atom
CAAnimation 简介:url
CAAimation
是全部动画对象的基类,负责控制动画的持续时间和速度,是个抽象类,不能直接使⽤用,应该使⽤用它具体的⼦子类
属性说明:spa
removedOnCompletion
// 动画是否⾃自动移出线程
duration
// 动画持续时间翻译
speed
//速度代理
timeOffset
// 动画时间的偏移code
repeatCount
// 动画重复执⾏行的次数(HUGE_VALF无限次)orm
repeatDuration
// 动画重复执⾏行的总时间
autoreverses
// 反转动画
delegate
// 代理
fillMode
// 填充模式
timingFunction
//速度控制函数
基本属性说明:
属性 | 说明 |
---|---|
duration |
动画的持续时间 |
repeatCount |
重复次数,无限循环能够设置HUGE_VALF或者MAXFLOAT |
repeatDuration |
重复时间 |
removedOnCompletion |
默认为YES,表明动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。若是想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置 |
fillMode |
决定当前对象在非active时间段的行为。好比动画开始以前或者动画结束之 |
beginTime |
能够用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间 |
timingFunction |
速度控制函数,控制动画运行的节奏 |
delegate |
动画代理 |
fillMode
属性的设置:
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到以前的状态kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 在动画开始前,只须要将动画加入了一个layer,layer便当即进入动画的初始状态并等待动画开始。
kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始以前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
速度控制函数(CAMediaTimingFunction):
kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感受
kCAMediaTimingFunctionEaseIn(加速):动画缓慢进入,而后加速离开
kCAMediaTimingFunctionEaseOut(减速):动画全速进入,而后减速的到达目的地
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,而后减速的到达目的地。这个是默认的动画行为。
CABasicAnimation 用于实现layer属性值从一个值(fromValue)到另一个值(toValue)变化的简单动画,好比旋转、缩放、逐渐透明、移动等。
相关属性:
fromValue:keyPath相应属性的初始值
byValue:keyPath相应属性的中间值( 变化的值)
toValue:keyPath相应属性的结束值
动画过程说明:
随着动画的进⾏行,在⻓长度为duration的持续时间内,keyPath相应属性的值从
fromValue渐渐地变为toValue。
keyPath内容是CALayer的可动画Animatable属性
若是fillMode=kCAFillModeForwards同时removedOnComletion=NO,那么
在动画执行完毕后,图层会保持显示动画执行后的状态。但实质上,图层属性值仍是动画执行前的初始值,并无真正被改变。
代码事例以下:
#import "ViewController.h" @interface ViewController (){ CALayer *aniLayer; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; aniLayer = [CALayer layer]; aniLayer.frame = CGRectMake(100, 50, 100, 100); aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage); [self.view.layer addSublayer:aniLayer]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 给layer 添加动画 [aniLayer addAnimation:[self positionAnimation] forKey:@"position"]; [aniLayer addAnimation:[self rotationAnimation] forKey:@"rotation"]; // [aniLayer addAnimation:[self boundsAnimation] forKey:@"bounds"]; // [aniLayer addAnimation:[self scaleAnimation] forKey:@"scale"]; } - (CAAnimation *)positionAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 50)]; animation.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 550)]; animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.autoreverses = YES; animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复 /* 动画的线性变换(动画速度变化) kCAMediaTimingFunctionLinear 匀速 kCAMediaTimingFunctionEaseIn 加速 kCAMediaTimingFunctionEaseOut 减速 kCAMediaTimingFunctionEaseInEaseOut 缓慢进入缓慢出去 kCAMediaTimingFunctionDefault 默认 */ animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; return animation; } - (CAAnimation *)boundsAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"]; animation.fromValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)]; animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)]; animation.duration = 2.0; return animation; } - (CAAnimation *)rotationAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; // animation.fromValue = @0; // animation.toValue = @(2 * M_PI); animation.byValue = @( -2 * M_PI); animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.autoreverses = YES; animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复 return animation; } - (CAAnimation *)scaleAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; // 一、初始值 animation.fromValue = @1.0; // 二、目标值 animation.toValue = @2.0; // 三、变化的值, fromValue ~ toValue 值的变化量 // animation.byValue = @1.0; // 四、动画时间 animation.duration = 2.0; /* 五、动画的填充模式: kCAFillModeForwards kCAFillModeBackwards kCAFillModeBoth kCAFillModeRemoved */ animation.fillMode = kCAFillModeForwards; // 六、动画后是否移除动画后的状态(回到原始状态),默认是YES, 前提是要设置fillModle为:kCAFillModeForwards animation.removedOnCompletion = NO; // 七、是否有回复效果 animation.autoreverses = YES; // 八、设置动画重复次数 animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复 // 九、播放动画的速度 animation.speed = 2; return animation; } @end
暂停动画:
-(void)stopAnimation{ // CACurrentMediaTime(): 当前媒体时间,表示系统启动后到当前的秒数,当系统重启后这个时间也重置 CFTimeInterval stopTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; // 设置动画的时间的偏移 layer.timeOffset = stopTime; layer.speed = 0; }
继续动画:
-(void)resumeAnimation{ // 获取暂停时的时间 CFTimeInterval stopTime = [layer timeOffset]; layer.speed = 1; layer.timeOffset = 0; layer.beginTime = 0; // 设置开始的时间(继续动画,这样设置至关于让动画等待的秒数等于暂停的秒) layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - stopTime; }
代码说明:
设置的animationWithKeyPath
是@"position",说明要修改的是CALayer的position属性,也就是会执行平移动画
animation.fromValue
,animation.toValue
这里的属性接收的时id类型的参数,所以并不能直接使用CGPoint这种结构体类型,而是要先包装成NSValue对象后再使用。
默认状况下,动画执行完毕后,动画会自动从CALayer上移除,CALayer又会回到原来的状态。为了保持动画执行后的状态,能够加入animation.removedOnCompletion = NO
CAKeyframeAnimation 能够给一个图层提供多个目标值(values)或者一个指定路径(path)的动画。关键帧动画有以下几个重要参数:
values
:指定图层属性(position、scale、rotation...)的多个目标值,这个图层就会由这些指定的值进行动画。
path
:一个CGPathRef
类型的路径,指定图层就会沿着这个路径进行动画。
keyTimes
:关键帧指定对应的时间点,其取值范围为0到1.0。也就是keyTimes中的每个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间平分。
代码事例以下:
#import "ViewController.h" #define TScreenWidth [UIScreen mainScreen].bounds.size.width #define TScreenHeight [UIScreen mainScreen].bounds.size.height @interface ViewController (){ CALayer *aniLayer; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CALayer *bgLayer = [CALayer layer]; bgLayer.frame = self.view.bounds; bgLayer.backgroundColor = [UIColor blackColor].CGColor; [self.view.layer addSublayer:bgLayer]; bgLayer.delegate = self; // 重绘 [bgLayer setNeedsDisplay]; aniLayer = [CALayer layer]; aniLayer.bounds = CGRectMake(0 , 0, 100, 100); aniLayer.position = CGPointMake(TScreenWidth / 2, 50); aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage); [self.view.layer addSublayer:aniLayer]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [aniLayer addAnimation:[self keyframeAnimation] forKey:@"keyAnimation"]; } - (CAAnimation *)keyframeAnimation { CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAnimation.duration = 4; // keyAnimation.autoreverses = YES; keyAnimation.repeatCount = HUGE_VALF; keyAnimation.fillMode = kCAFillModeForwards; keyAnimation.removedOnCompletion = NO; /* NSValue *point_1 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)]; NSValue *point_2 = [NSValue valueWithCGPoint:CGPointMake(50,TScreenHeight / 2)]; NSValue *point_3 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,TScreenHeight - 50)]; NSValue *point_4 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth - 50,TScreenHeight / 2)]; NSValue *point_5 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)]; // values:设置关键帧(多个目标点) keyAnimation.values = @[point_1,point_2,point_3,point_4,point_5]; // 设置每一帧所在的时间比例 keyAnimation.keyTimes = @[@0, @0.2, @0.5, @0.6,@1.0]; */ /* 插值计算模式: kCAAnimationLinear 关键帧之间进行插值计算(线性的) kCAAnimationDiscrete 关键帧之间不进行插值计算(离散的) kCAAnimationPaced 关键帧之间匀速切换,keyTimes\timingFunctions的设置将不起做用 kCAAnimationCubic 关键帧进行圆滑曲线相连后插值计算 kCAAnimationCubicPaced 匀速而且关键帧进行圆滑曲线相连后插值计算 */ keyAnimation.calculationMode = kCAAnimationLinear; keyAnimation.path = [self path].CGPath; return keyAnimation; } // 绘制路径 - (UIBezierPath *)path { // 椭圆 // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.view.bounds]; // 圆角矩形 // UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:50]; // 内切圆 // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 100, 300, 300)]; // 贝塞尔曲线 UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, TScreenHeight)]; CGPoint point_1 = CGPointMake(TScreenWidth, TScreenHeight); CGPoint controPoint_1 = CGPointMake(TScreenWidth / 2, - TScreenHeight); // CGPoint controPoint_2 = CGPointMake(TScreenWidth / 4 * 3, TScreenHeight); [path addQuadCurveToPoint:point_1 controlPoint:controPoint_1]; // [path addCurveToPoint:point_1 controlPoint1:controPoint_1 controlPoint2:controPoint_2]; return path; } // 绘图 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextAddPath(ctx , [self path].CGPath); CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); CGContextSetLineWidth(ctx, 5); CGContextDrawPath(ctx, kCGPathStroke); } @end
CAKeyframeAnimation——计算模式属性
在关键帧动画中还有一个很是重要的参数,那即是calculationMode,所谓计算模式:其主要针对的是每一帧的内容为一个座标点的状况,也就是对anchorPoint和 position进行的动画
当在平面座标系中有多个离散的点的时候,能够是离散的,也能够直线相连后进行插值计算,也可使用圆滑的曲线将他们相连后进行插值计算
calculationMode目前提供以下几种模式:
kCAAnimationLinear: 默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算
kCAAnimationDiscrete: 离散的,不进行插值计算,全部关键帧直接逐个进行显示
kCAAnimationPaced :使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效
kCAAnimationCubic: 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑
kCAAnimationCubicPaced :看这个名字就知道和kCAAnimationCubic有必定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的
CATransition是CAAnimation的子类,用于作转场动画
可以为图层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点
如:UINavigationController导航控制器就是经过CATransition转场动画实现了将控制器的视图推入屏 幕的动画效果
转场动画就是从一个场景以动画的形式过渡到另外一个场景。转场动画的使用通常分为如下几个步骤:
1.建立转场动画 CATransition
2.设置转场类型transtion.type、子类型transtion.subtype(可选)及其余属性
3.设置转场后的新视图并添加动画到图层
下表列出了经常使用的转场类型(注意私有API是苹果官方没有公开的动画类型,可是目前经过仍然可使用):
fade 淡出效果 kCATransitionFade
movein 新视图移动到旧视图上 kCATransitionMoveIn
push 新视图退出旧视图上 kCATransitionPush
reveal 移开旧视图显示新视图 kCATransitionReveal
cube 立体翻转效果
oglFlip 翻转效果
suckEffect 收缩效果
rippleEffect 水滴波纹效果
pageCurl 向上翻页效果
pageUnCurl 向下翻页效果
cameralIrisHollowOpen 摄像头打开效果
cameraIrisHollowClose 摄像头关闭效果
代码事例以下:
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { static int index = 1; [self.imageView.layer addAnimation:[self transitionAnimation] forKey:nil]; NSString *imageName = [NSString stringWithFormat:@"large_%d.jpg",index]; self.imageView.image = [UIImage imageNamed:imageName]; index++; if (index > 10) { index = 0; } } - (CAAnimation *)transitionAnimation { CATransition *transitionAni = [CATransition animation]; transitionAni.duration = 1.0; /* 1. fade 淡出效果 2. moveIn 进入效果 3. push 推出效果 4. reveal 移出效果 // 未公开的 5. cube 立方体翻转效果 6. suckEffect 抽走效果 7. rippleEffect 水波效果 8. pageCurl 翻开页效果 9. pageUnCurl 关闭页效果 10. cameraIrisHollowOpen 相机镜头打开效果 11. cameraIrisHollowClose 相机镜头关闭效果 */ transitionAni.type = kCATransitionPush; // transitionAni.type = @"push"; // 转场的方向:`fromLeft', `fromRight', `fromTop' `fromBottom' transitionAni.subtype = @"fromTop"; // 开始转场和结束转场的进度位置 // transitionAni.startProgress = 0.5; // transitionAni.endProgress = 1; return transitionAni; } @end
CATransition是CAAnimation的子类,用于作转场动画,可以为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。
UINavigationController就是经过CATransition实现了将控制器的视图推入屏幕的动画效果。
2.subtype:动画过渡⽅方向
3.startProgress:动画起点
4.endProgress:动画终点
1.kCATransitionFade:淡⼊入淡出
2.kCATransitionMoveIn:新视图移到旧视图上⾯面
3.kCATransitionPush:新视图把旧视图推出
4.kCATransitionReveal:把旧视图移开,显⽰示下⾯面的新视图
代码事例以下:
#import "ViewController.h" #define TScreenWidth [UIScreen mainScreen].bounds.size.width #define TScreenHeight [UIScreen mainScreen].bounds.size.height @interface ViewController () @property (weak, nonatomic) IBOutlet UIView *bgView; @property (weak, nonatomic) IBOutlet UIImageView *aniImageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.bgView.layer.delegate = self; [self.bgView.layer setNeedsDisplay]; NSLog(@"%@",NSStringFromCGRect(self.aniImageView.frame)); NSLog(@"%f, %f",TScreenWidth, TScreenHeight); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.aniImageView.layer addAnimation:[self animationGroup] forKey:@"group"]; } - (CAAnimation *)positionAnimation { CAKeyframeAnimation *keyAni = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAni.duration = 3.0; keyAni.fillMode = kCAFillModeForwards; keyAni.removedOnCompletion = NO; keyAni.path = [self path].CGPath; return keyAni; } - (CAAnimation *)rotationAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.beginTime = 3.0; animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.byValue = @(2 * M_PI); return animation; } - (CAAnimation *)downAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.beginTime = 3.0; animation.duration = 2.0; animation.byValue = [NSValue valueWithCGPoint:CGPointMake(0, TScreenHeight)]; return animation; } - (UIBezierPath *)path { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, TScreenHeight)]; CGPoint toPoint = CGPointMake(TScreenWidth, 0); CGPoint controlPoint = CGPointMake(TScreenWidth,TScreenHeight); [path addQuadCurveToPoint:toPoint controlPoint:controlPoint]; return path; } - (CAAnimation *)animationGroup { // 建立一个动画组,用于组合多种动画 CAAnimationGroup * aniGroup = [CAAnimationGroup animation]; // 动画组的完成时间 aniGroup.duration = 5.0; aniGroup.fillMode = kCAFillModeForwards; aniGroup.removedOnCompletion = NO; // 组合多个动画 aniGroup.animations = @[[self positionAnimation],[self rotationAnimation],[self downAnimation]]; return aniGroup; } - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextAddPath(ctx, [self path].CGPath); CGContextSetLineWidth(ctx, 5); CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); CGContextDrawPath(ctx, kCGPathStroke); } @end