iOS_核心动画(二)
目 录:
一、Core Animation开发步骤
二、Core Animation的继承结构
三、CAAnimation常用的属性
四、CAPropertyAnimation(属性动画)
五、CABasicAnimation(基本动画)
六、CAKeyframeAnimation(关键帧动画)
七、CAAnimationGroup(动画组)
八、CATransition(转场动画)
上节中分享了核心动画的基础部分CALayer,知道了核心动画的操作对象不是UIView,而是基于CALayer。今天来看下Core Animation如何实现动画效果的。
注意:图中的黑色虚线代表“继承”某个类,红色虚线代表“遵守”某个协议。
从上图可以看出CAAnimation时所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,如果设置动画应该使用它具体的子类。
(1)CAAnimation:它是所有动画类的基类,它实现了CAMediaTiming协议,提供了动画的持续时间、速度和重复计数等。CAAnimation还实现了CAAction协议,该协议为CALayer动画触发的动作提供标准化响应。
(2)CATransition:可以通过预置的过渡效果来控制CALayer层的过渡动画。
(3)CAPropertyAnimation:它代表一个属性动画,可通过+animationWithKeyPath:类方法来创建属性动画实例(程序一般创建该子类的实例),该方法需要指定一个CALayer支持动画的属性,然后通过它的子类控制CALayer的动画属性慢慢睇改变,即可实现CALayer动画。
(4)CBasicAnimation:简单地控制CALayer层的属性慢慢改变,从而实现动画效果。很多CALayer层的属性值的修改默认会执行这个动画类。比如大小、透明度、颜色等属性。
(5)CAKeyframeAnimation:支持关键帧的属性动画,该动画的最大特点在于可通过values属性指定多个关键帧,通过多个关键帧可以指定动画的各阶段的关键值。
(6)CAAnimationGroup:用于将多个动画组合在一起执行。
三、CAAnimation常用的属性(红色代表来自CAMediaTiming协议的属性)。
(1) duration:动画的持续时间
(2) repeatCount:重复的次数,无线循环可以设置HUGE_VALF或者MAXFLOAT
(3)repeatDuration:重复时间
(4)removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards(产生一个动画将CALayer移动到另外一个位置,而CALayer的实际位置却没有改变,仍然在动画开启前的位置)
默认值为0,无延迟
#import "ViewController.h" @interface ViewController () @property(strong,nonatomic)CALayer *subLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //创建子层 self.subLayer = [CALayer layer]; self.subLayer.bounds = CGRectMake(0, 0, 100, 100); self.subLayer.position = CGPointMake(100, 100); self.subLayer.cornerRadius = 50; self.subLayer.backgroundColor = [[UIColor greenColor]CGColor]; [self.view.layer addSublayer:self.subLayer]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)]; tap.numberOfTapsRequired = 1; //接受几个手指响应的事件 tap.numberOfTouchesRequired = 1; [self.view addGestureRecognizer:tap]; } -(void)tap:(UITapGestureRecognizer *)sender { CGPoint location = [sender locationInView:self.view]; CABasicAnimation *basic = [[CABasicAnimation alloc]init]; basic.duration = 3.0;//动画时长 basic.keyPath = @"position"; basic.removedOnCompletion = NO; basic.fillMode = kCAFillModeForwards; basic.delegate = self; //将动画的结束位置移动到点击的坐标处 basic.toValue = [NSValue valueWithCGPoint:location]; [self.subLayer addAnimation:basic forKey:@"basic"]; } - (IBAction)pauseClicked:(UIButton *)sender { if (sender.tag == 0) { //动画暂停 [self animationPause]; sender.tag = 1; } else { //动画恢复 [self animationResume]; sender.tag = 0; } } //动画暂停 -(void)animationPause { //获取当前暂停的时间段 CFTimeInterval pauseTime = [self.subLayer convertTime:CACurrentMediaTime() fromLayer:nil]; NSLog(@"pausetTime:%f",pauseTime); //将速度降为0,动画停止 self.subLayer.speed = 0.0; //保存暂停时间 self.subLayer.timeOffset = pauseTime; } -(void)animationResume { CFTimeInterval pauseTime = self.subLayer.timeOffset; self.subLayer.timeOffset = pauseTime; self.subLayer.speed = 1.0; self.subLayer.beginTime = 0.0; //计算开始时间 CFTimeInterval beginTime = [self.subLayer convertTime:CACurrentMediaTime() fromLayer:nil]-pauseTime; self.subLayer.beginTime = beginTime; // NSLog(@"beginTime:%@",beginTime); } #pragma mark - 动画代理的方法 //动画开始 -(void)animationDidStart:(CAAnimation *)anim { NSLog(@"animationDidStart"); } //动画结束 -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { // NSLog(@"%@",((CABasicAnimation *)anim).toValue); NSValue *toValue = ((CABasicAnimation *)anim).toValue; CGPoint location = [toValue CGPointValue]; self.subLayer.position = location; } @end
六、 CAKeyframeAnimation(关键帧动画)
关键帧动画也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
属性说明:
- values:上述的NSArray对象。里面的元素称为“帧”。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧。
- path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和positon起作用。如果设置了path,那么values将被忽略
- keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的。
#import "ViewController.h" #define MAX_TOUCH_NUM 5//最大点击次数 @interface ViewController () @property(strong,nonatomic)NSMutableArray *points; @property(strong,nonatomic)CALayer *subLayer; @property(assign,nonatomic)CGMutablePathRef path; @property(strong,nonatomic)CALayer *drawLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //添加绘图层 self.drawLayer = [CALayer layer]; self.drawLayer.bounds = self.view.bounds; self.drawLayer.position = self.view.center; self.drawLayer.backgroundColor = [[UIColor whiteColor]CGColor]; [self.view.layer addSublayer:self.drawLayer]; //创建子层 self.subLayer = [CALayer layer]; self.subLayer.bounds = CGRectMake(0, 0, 100, 100); self.subLayer.position = CGPointMake(100, 100); self.subLayer.backgroundColor = [[UIColor greenColor]CGColor]; [self.view.layer addSublayer:self.subLayer]; self.points = [NSMutableArray array]; // //设置跟layer的代理 self.drawLayer.delegate = self; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self.view]; //创建路径 self.path = CGPathCreateMutable(); CGPathMoveToPoint(self.path, nil, location.x, location.y); } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { //将当前点加到路径中去 UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self.view]; CGPathAddLineToPoint(self.path,nil, location.x, location.y); [self.drawLayer setNeedsDisplay]; } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //创建帧动画(路径动画) CAKeyframeAnimation *keyFrame = [[CAKeyframeAnimation alloc]init]; keyFrame.keyPath = @"position"; keyFrame.path = self.path; keyFrame.duration = 3.0f; //设置动画保存在最后的位置 keyFrame.removedOnCompletion = NO; keyFrame.fillMode = kCAFillModeForwards; keyFrame.delegate = self; [self.subLayer addAnimation:keyFrame forKey:@"keyFrame"]; } -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { CGPathRelease(self.path); } #pragma mark - 层绘制的代理方法 -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextAddPath(ctx, self.path); CGContextSetStrokeColorWithColor(ctx, [[UIColor blueColor]CGColor]); CGContextDrawPath(ctx, kCGPathStroke); } @end
运行效果图,如下:
七、CAAnimationGroup动画组
动画组是CAAnimation子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中的所有动画可以同时并发运行。
属性说明:
- animations:用来管理多个动画对象的NSArray。默认情况下,多个动画是同时执行的,可以通过设置动画对象的beginTime来改变动画的开始执行时间。
案例分析:
#import "ViewController.h" @interface ViewController () @property(strong,nonatomic)CALayer *subLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //创建子层 self.subLayer = [CALayer layer]; self.subLayer.bounds = CGRectMake(0, 0, 100, 100); self.subLayer.position = CGPointMake(100, 100); self.subLayer.backgroundColor = [[UIColor blueColor]CGColor]; [self.view.layer addSublayer:self.subLayer]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)]; tap.numberOfTapsRequired = 1; tap.numberOfTouchesRequired = 1; [self.view addGestureRecognizer:tap]; } //创建旋转动画 -(CABasicAnimation *)rotationAnimationFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue { //创建基本动画 CABasicAnimation *animationRotation = [[CABasicAnimation alloc]init]; animationRotation.keyPath = @"transform.rotation.z"; animationRotation.fromValue = @(fromValue); animationRotation.toValue = @(toValue); animationRotation.duration = 1.0f; animationRotation.removedOnCompletion = NO; animationRotation.fillMode = kCAFillModeForwards; return animationRotation; } //创建缩放动画 -(CABasicAnimation *)scaleAnimationFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue { CABasicAnimation *scaleAnimation = [[CABasicAnimation alloc]init]; scaleAnimation.keyPath = @"transform.scale"; scaleAnimation.fromValue = @(fromValue); scaleAnimation.toValue = @(toValue); scaleAnimation.duration = 2.0f; scaleAnimation.removedOnCompletion = NO; scaleAnimation.fillMode = kCAFillModeForwards; return scaleAnimation; } -(void)tap:(UITapGestureRecognizer *)sender { //创建动画组 CAAnimationGroup *group = [[CAAnimationGroup alloc]init]; NSArray *animations = @[[self rotationAnimationFromValue:-M_PI toValue:-M_PI_4], [self scaleAnimationFromValue:1.0 toValue:2.0]]; group.animations = animations; //动画组中的动画的属性,受动画组的控制(比如:动画内设置了大小,时长,在动画组内不起作用,以动画组设置为准) group.duration = 4.0f; // group.removedOnCompletion = NO; // group.fillMode = kCAFillModeForwards; //延时执行 // group.beginTime = CACurrentMediaTime()+1.0f; [self.subLayer addAnimation:group forKey:@"animation"]; } @end
八、CATransition 转场动画
CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS 比Mac OSX的转场动画效果少一点。
#import "ViewController.h" @interface ViewController () @property(strong,nonatomic)UIImageView *imageView; @property(assign,nonatomic)NSInteger index; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.index = 0; //创建imageView; self.imageView = [[UIImageView alloc]initWithFrame:self.view.frame]; self.imageView.contentMode = UIViewContentModeScaleAspectFit; self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",self.index]]; self.imageView.userInteractionEnabled = YES; [self.view addSubview:self.imageView]; UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)]; leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft; [self.imageView addGestureRecognizer:leftSwipe]; UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)]; rightSwipe.direction = UISwipeGestureRecognizerDirectionRight; [self.imageView addGestureRecognizer:rightSwipe]; } -(void)leftSwipe:(UISwipeGestureRecognizer *)sender { if (sender.direction == UISwipeGestureRecognizerDirectionLeft) { if (self.index > 0) { self.index--; } } else { if (self.index < 9) { self.index++; } } //过场动画 CATransition *transition = [[CATransition alloc]init]; //动画类型 transition.type = @"cube"; //设置方向 transition.subtype = kCATransitionFromBottom; //设置时长 transition.duration = 0.5f; //添加动画 [self.imageView.layer addAnimation:transition forKey:@"transition"]; self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",self.index]]; } @end
到此,核心动画就完结了。