本文参考:http://www.cnblogs.com/kenshincui/p/3972100.html#autoid-3-0-0总结的:
Core Animation
*
iOS 核心动画的实现
CoreAnimation (包含在Quartz Core 框架中), 在iOS核心动画分为几类(基础动画, 关键帧动画, 动画组, 转场动画, )
CAAnimation : 核心动画的基础类, 不能直接用, 负责动画运行时间吗速度的控制, 实现了CAMediaTiming协议
CAPropertyAnimation : 属性动画的基类 (通过属性进行动画设置, 注意是可动画属性), 不能直接使用
CAAnimationGroup : 动画组, 动画组是一种组合模式设计, 可以通过动画组来进行所有动画行为的统一控制, 组中所有动画效果可以并发执行(一起执行)
CATransition : 转场动画, 主要通过滤镜进行动画效果设置
CABasicAnimation : 基础动画, 通过属性修改进行动画参数控制, 只有初始状态和结束状态
CAKeyframeAnimation : 关键帧动画, 同样是通属性进行动画采纳数控制, 但同基础动画不同的是他可以多个状态控制
基础动画, 关键帧动画, 都属于属性动画, 就是通过修改属性值产生动画效果, 开发人员只需要设置初始值和结束值, 中间过程动画(补间动画)由系统自动计算产生, 和基础动画不同的是关键帧动画可以设置多个属性值, 每两个属性中间的补间动画由系统自定完成, 因此从这个角度基础动画是又来个关键帧的关键帧动画
直接上代码,注释很全, 简单易懂:
<span style="font-size:24px;">// // ViewController.m // CoreAnimation // // Created by 帝炎魔 on 16/5/25. // Copyright © 2016年 帝炎魔. All rights reserved. // #import "ViewController.h" #define WIDTH 50 // 宽度 @interface ViewController () @property (nonatomic, strong) CALayer *basicAniLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor]; // 小圆球的动画 // [self drawMyLayer]; // 使用代理方法绘图 // [self drawRollImageView]; // 基础动画 [self BasiceAnimation]; } /** * Core Animation * iOS 核心动画的实现 CoreAnimation (包含在Quartz Core 框架中), 在iOS核心动画分为几类(基础动画, 关键帧动画, 动画组, 转场动画, ) CAAnimation : 核心动画的基础类, 不能直接用, 负责动画运行时间吗速度的控制, 实现了CAMediaTiming协议 CAPropertyAnimation : 属性动画的基类 (通过属性进行动画设置, 注意是可动画属性), 不能直接使用 CAAnimationGroup : 动画组, 动画组是一种组合模式设计, 可以通过动画组来进行所有动画行为的统一控制, 组中所有动画效果可以并发执行(一起执行) CATransition : 转场动画, 主要通过滤镜进行动画效果设置 CABasicAnimation : 基础动画, 通过属性修改进行动画参数控制, 只有初始状态和结束状态 CAKeyframeAnimation : 关键帧动画, 同样是通属性进行动画采纳数控制, 但同基础动画不同的是他可以多个状态控制 基础动画, 关键帧动画, 都属于属性动画, 就是通过修改属性值产生动画效果, 开发人员只需要设置初始值和结束值, 中间过程动画(补间动画)由系统自动计算产生, 和基础动画不同的是关键帧动画可以设置多个属性值, 每两个属性中间的补间动画由系统自定完成, 因此从这个角度基础动画是又来个关键帧的关键帧动画 */ #pragma mark --- 基础动画 /** * 动画创建的步骤 * 1. 初始化动画并设置动画属性 2. 设置动画属性初始值(可以省略), 结束值以及其他动画属性 3. 给图层添加动画 */ - (void)BasiceAnimation { // 设置背景 (注意这个图片是在根视图) UIImage *backImage = [UIImage imageNamed:@"haha1"]; self.view.backgroundColor = [UIColor colorWithPatternImage:backImage]; // 自定义一个图层 _basicAniLayer = [[CALayer alloc] init]; _basicAniLayer.bounds = CGRectMake(0, 0, 50, 50 ); _basicAniLayer.position = CGPointMake(50, 150); _basicAniLayer.contents = (id)[UIImage imageNamed:@"hudie"].CGImage; [self.view.layer addSublayer:_basicAniLayer]; } #pragma mark ---- 移动动画 - (void)translationAnimation:(CGPoint)location { // 1.创建动画并制定动画属性(通过keyPath 制定calayer的某个属性动画) CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; // 2. 设置其他动画属性初始值和结束值 basicAnimation.fromValue = [NSValue valueWithCGPoint:_basicAniLayer.position]; // 可以不设置, 默认为图层初始值状态 basicAnimation.toValue = [NSValue valueWithCGPoint:location]; NSLog(@"%f----%f", _basicAniLayer.position.x, _basicAniLayer.position.y); // 设置其他动画属性 basicAnimation.duration = 2.0; // 动画时间2秒 // basicAnimation.repeatCount = HUGE_VALF; // 设置动画重复的次数 HUGE_VALF代表无限大,无线重复动画 // basicAnimation.removedOnCompletion = NO; // 运行一次是否移除动画 /** * 这个动画存在一个问题: 动画结束后画图层回到了原来的位置, 当然用UIVieW封装的方法是没有这个问题的, 如何解决这个问题呢 * 图层动画的本质就是将图层内部的内容转化为位图硬件操作形成一种动画效果, 其实图层本身并没有任何的变化,上面的动画中图层并没有因为动画效果而改变它的位置, (对于缩放动画其大小也是不会改变的), 所以在动画完成之后, 图层的属性也不会有任何变化, 我们可以在动画完成之后重新设置它的位置 */ basicAnimation.delegate = self; // 存储当前位置在动画结束后使用 [basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"]; // 添加动画到图层, 注意key相当于给动画进行命名, 以后获得该动画是可以使用此名称获取 [_basicAniLayer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"]; // 在touch 点击事件中 执行动画 } #pragma mark ---- 旋转动画 /** * 图层的形变都是基于锚点进行的 * 通过keyPath 对锚点进行改变, 旋转的中心点就是图层的锚点 */ - (void)rotationAnimation { // 1. 创建动画并制定动画属性 CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; // 2. 设置动画属性初始值, 终止值 basicAnimation.fromValue = [NSNumber numberWithInt:M_PI_2]; basicAnimation.toValue = [NSNumber numberWithInt:M_PI_2 * 3]; // 3. 设置其他动画属性 basicAnimation.duration = 6.0; // 设置旋转后再旋转到原来的位置 basicAnimation.autoreverses = true; basicAnimation.repeatCount = HUGE_VALF; // 设置无线循环 // 动画运行一次不销毁, 默认为YES动画运行一次直接销毁 basicAnimation.removedOnCompletion = NO; // 给图层添加动画 key相当于给动画命名, 可以通过key得到该动画 [_basicAniLayer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"]; } /** * 核心动画的运行有一个媒体时间的概念, 假设将一个旋转动画设置旋转一周用时60秒的话, 那么当动画旋转90度的时候, 媒体时间就是15秒,如果此时要将动画暂停只需要让媒体时间偏移量设置为15秒即可, 并把动画运行速度设置为0让它停止运动,类似的, 如果又过了60妙需要恢复动画(此时媒体时间为75妙), 这时只要将动画开始开始时间设置为当前媒体时间75秒减去暂停时的时间*也就是之前定格动画时的偏移量15妙. 75-15=60秒. 这时对暂停动画和恢复动画的其实是动画速度的调整, 媒体时间偏移量以及恢复时的开始时间设置主要为了让动画更加连贯. * * */ #pragma mark --- 动画暂停 - (void)animationPause { // 取得指定图层动画的媒体时间, 后面参数用于指定子图层, 这里不需要 CFTimeInterval interval = [_basicAniLayer convertTime:CACurrentMediaTime() fromLayer:nil]; // 设置时间偏移量, 保证暂停时停留在旋转的位置 [_basicAniLayer setTimeOffset:interval]; // 速度设置为0 暂停动画 _basicAniLayer.speed = 0; } #pragma mark --- 动画恢复 - (void)animationResume { // 取得指定图层动画的媒体时间, 后面的参数用于指定子图层, 这里不需要 CFTimeInterval interval = CACurrentMediaTime() - _basicAniLayer.timeOffset; // 设置时间偏移量 _basicAniLayer.timeOffset = 0; // 设置开始时间 _basicAniLayer.beginTime = interval; // 设置动画速度, 开始运动 _basicAniLayer.speed = 1.0; } #pragma mark ---- 动画代理方法 // 动画开始的时候 -(void)animationDidStart:(CAAnimation *)anim { NSLog(@"animation (%@) start.\r_layer.frame=%@", anim, NSStringFromCGRect(_basicAniLayer.frame)); // 通过前面设置的key获得动画 // 设置动画的图层 调用 animationForKey:@"图层设置的动画Key" NSLog(@"%@", [_basicAniLayer animationForKey:@"KCBasicAnimation_Translation"]); } // 动画结束的时候 -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { /** * 还有一个问题就是动画运行完成后会重新从起始点运动到终点, 这个问题产生原因就是前面提到的,对于非根视图, 设置图层的可动画属性(在动画结束后重新设置了position, 而position是可动画的属性,会产生动画效果),解决这个问题就是在重新设置这个图层的position关闭图层隐式动画 * * 要关闭隐式动画需要用到动画事务CATransaction, 在事务内将隐式动画关闭 1. 开启事务 [CATransaction begin]; 2. 禁用隐式动画 [CATransaction setDisableActions:YES]; 3. 事务提交 [CATransaction commit]; */ NSLog(@"animation (%@) start.\r_layer.frame=%@", anim, NSStringFromCGRect(_basicAniLayer.frame)); // 开启事务 [CATransaction begin]; // 禁用隐式动画 [CATransaction setDisableActions:YES]; _basicAniLayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue]; CABasicAnimation *animation = (CABasicAnimation *)[anim valueForKey:@"KCBasicAnimation_Translation"]; animation.fromValue = [NSValue valueWithCGPoint:[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue]];; // 提交事务 [CATransaction commit]; // 暂停动画 [self animationPause]; } #pragma mark --- 使用代理方法绘图 - (void)drawRollImageView { // 自定义图层 CALayer *layer = [[CALayer alloc] init]; layer.bounds = CGRectMake(0, 0, 150, 150); layer.position = CGPointMake(160, 200); layer.backgroundColor = [UIColor redColor].CGColor; layer.cornerRadius = 150 / 2; // 需要裁剪圆角多余的部分 layer.masksToBounds = YES; // 如果设置多余图片裁剪的效果的话, 无法设置图片的阴影 // 如果想设置阴影效果的话, 再添加一个layer 与该图片图层等大, 再设置图层 // 下面的图层负责绘制阴影, 上面的图层负责显示图片 // 注意 : 利用图层形变解决图像倒立问题 没必要走drawRect的方法 // layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0); // 注意 : 如果只是显示一张图片在图层中, 没必要麻烦, 直接设置图层contents就可以了 // 不会牵制到绘图, 就不会涉及到倒立的问题 // 设置内容 // UIImage *image = [UIImage imageNamed:@"jiqimao"]; // [layer setContents:(id)image.CGImage]; // 设置图层代理 layer.delegate = self; // 添加图层到根图层 [self.view.layer addSublayer:layer]; // 调用图层setNeedDisplay, 否则代理方法不会走 [layer setNeedsDisplay]; } // layer [layer setNeedsDisplay]的代理方法 一定要调用这个方法 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { // layer 就是设置代理的那个图层 // CGContextSaveGState(ctx); // 图形上下文形变, 解决图片倒立问题 CGContextScaleCTM(ctx, 1, -1); CGContextTranslateCTM(ctx, 0, -150); UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"jiqimao"]]; // imageView.bounds = CGRectMake(0, 0, 150, 150); // 注意这个位置是相对图层而言, 不是屏幕 CGContextDrawImage(ctx, CGRectMake(0, 0, 150, 150),imageView.image.CGImage); // CGContextRestoreGState(ctx); } #pragma mark 绘制圆形图层, 创建动画 -(void)drawMyLayer{ CGSize size=[UIScreen mainScreen].bounds.size; //获得根图层 CALayer *layer=[[CALayer alloc]init]; //设置背景颜色,由于QuartzCore是跨平台框架,无法直接使用UIColor layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor; //设置中心点 layer.position=CGPointMake(size.width/2, size.height/2); //设置大小 layer.bounds=CGRectMake(0, 0, WIDTH,WIDTH); //设置圆角,当圆角半径等于矩形的一半时看起来就是一个圆形 layer.cornerRadius=WIDTH/2; //设置阴影 layer.shadowColor=[UIColor grayColor].CGColor; layer.shadowOffset=CGSizeMake(2, 2); layer.shadowOpacity=.9; //设置边框 // layer.borderColor=[UIColor whiteColor].CGColor; // layer.borderWidth=1; //设置锚点 // layer.anchorPoint=CGPointZero; [self.view.layer addSublayer:layer]; } #pragma mark --- touch方法 // 点击放大效果 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ // 1. 点击移动动画 // UITouch *touch=[touches anyObject]; // CALayer *layer=[self.view.layer.sublayers lastObject]; // CGFloat width=layer.bounds.size.width; // if (width==WIDTH) { // width=WIDTH*4; // }else{ // width=WIDTH; // } // layer.bounds=CGRectMake(0, 0, width, width); // layer.position=[touch locationInView:self.view]; // // layer.cornerRadius=width/2; // 2. 基础动画 UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.view]; // 判断是否已创建过动画, 如果已经创建则不创建动画 CAAnimation *animation = [_basicAniLayer animationForKey:@"KCBasicAnimation_Translation"]; if (animation) { if (_basicAniLayer.speed == 0) { [self animationResume]; } else { [self animationPause]; } } else { // 创建并开始动画 [self translationAnimation:point]; [self rotationAnimation]; } } @end </span>
时间: 2024-10-11 17:58:11