iOS 核心动画

CALayer的基本属性

player中有两个重要的属性,position和anchorPoint,position决定位置,假如图层要旋转时anchorPoint则是旋转的点。

假如锚点anchorPoint为默认值即中点(0.5,0.5),而该层的position设置为(0,0)即为父层的左上点,那么该层在父层中只会看到四分之一的部分。


Core Animation结构


其中灰色虚线表示继承关系,红色表示遵守协议。

核心动画中所有类都遵守CAMediaTiming协议。
CAAnaimation是个抽象类,不具备动画效果,必须用它的子类才有动画效果。

CAAnimationGroup和CATransition才有动画效果,CAAnimationGroup是个动画组,可以同时进行缩放,旋转(同时进行多个动画)。

CATransition是转场动画,界面之间跳转(切换)都可以用转场动画。

CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类才有。

扫描二维码关注公众号,回复: 631280 查看本文章

CABasicAnimation和CAKeyframeAnimation:
CABasicAnimation基本动画,做一些简单效果。
CAKeyframeAnimation帧动画,做一些连续的流畅的动画。

他们的基本使用和现实生活中画画是非常类似的

第1步:准备纸

CALayer *scaleLayer = [[CALayer alloc] init];
scaleLayer.backgroundColor = [UIColor blueColor].CGColor;
scaleLayer.frame = CGRectMake(60, 20, 50, 50);
scaleLayer.cornerRadius = 10;
[self.view.layer addSublayer:scaleLayer];

第2步:准备笔(这里用笔规划出路径)
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale”];//keyPath不能填错,有特定被选值
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.5];
scaleAnimation.autoreverses = YES;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.removedOnCompletion = NO;
scaleAnimation.repeatCount = MAXFLOAT;
scaleAnimation.duration = 0.8;
第3步:开始画
[scaleLayer addAnimation:scaleAnimation forKey:@"scaleAnimation”];

代码中有几个参数需要解释一下:
1.keyPath: 决定基础动画的类型,该值不能随便取,一旦取错就达不到想要的效果。要改变位置就取position,要改变透明度就取opacity,要等比例缩放就取transform.scale...
2.fromValue: 动画的起始状态值,虽然iOS文档给出的类型是id,不过这里应该传NSValue对象,比如NSNumber(NSNubmer继承自NSValue)
3. autoreverse: 当动画执行到toValue指定的状态时是从toValue的状态逆回去,还是直接跳到fromValue的状态再执行一遍
4. fileMode: fillMode的作用就是决定当前对象过了非active时间段的行为. 非active时间段是指动画开始之前以及动画结束之后。如果是一个动画CAAnimation,则需要将其removedOnCompletion设置为NO,要不然fillMode不起作用. 下面来讲各个fillMode的意义:
  • kCAFillModeRemoved   默认值为这个,这个是在动画开始前和动画结束后,动画对layer都没有影响,动画结束后恢复到之前的状态

  • kCAFillModeForwards   当前动画结束后,layer会一直保持着最后的状态

  • kCAFillModeBackwards  与kCAFillModeForwards是相对的,就是在动画开始前,将动画加入一个layer,layer便进入动画的初始状态,因为有可能出现fromValue不是目前layer的初始状态的情况,如果fromValue就是layer当前的状态,则这个参数就没太大意义

  • kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.

代码的例子



下面是手指画出路径之后,layer跟着路径运动的代码

#import "DrawView.h"


@interface DrawView ()


@property (nonatomic, strong) UIBezierPath *path;


@end


@implementation DrawView


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    // touch

    UITouch *touch = [touches anyObject];

    // 获取手指的触摸点

    CGPoint curP = [touch locationInView:self];

    // 创建路径

    UIBezierPath *path = [UIBezierPath bezierPath];

    _path = path;

    // 设置起点

    [path moveToPoint:curP];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

    // touch

    UITouch *touch = [touches anyObject];

    // 获取手指的触摸点

    CGPoint curP = [touch locationInView:self];

    [_path addLineToPoint:curP];

    [self setNeedsDisplay];

}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

    // imageView添加核心动画

    // 添加核心动画

    CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];

    anim.keyPath = @"position";

    //    anim.values = @[@(angle2Radion(-10)),@(angle2Radion(10)),@(angle2Radion(-10))];

    anim.path = _path.CGPath;

    anim.duration = 1;

    anim.repeatCount = MAXFLOAT;

    [[[self.subviews firstObject] layer] addAnimation:anim forKey:nil];

}

- (void)drawRect:(CGRect)rect

{

    [_path stroke];

}

@end


转场动画

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    // 转场代码

    if (i == 4) {

        i = 1;

    }

    // 加载图片名称

    NSString *imageN = [NSString stringWithFormat:@"%d",i];

    _imageView.image = [UIImage imageNamed:imageN];

    i++;

    // 转场动画

    CATransition *anim = [CATransition animation];

    anim.type = @"pageCurl";

    anim.duration = 2;

    [_imageView.layer addAnimation:anim forKey:nil];

}


组动画(第一步准备画纸省略了)

缩放
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@ "transform.scale" ];
     scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
     scaleAnimation.toValue = [NSNumber numberWithFloat:1.5];
     scaleAnimation.autoreverses = YES;
     scaleAnimation.repeatCount = MAXFLOAT;
     scaleAnimation.duration = 0.8;
移动

CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@ "position" ];
     moveAnimation.fromValue = [NSValue valueWithCGPoint:groupLayer.position];
     moveAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(320 - 80, groupLayer.position.y)];
     moveAnimation.autoreverses = YES;
     moveAnimation.repeatCount = MAXFLOAT;
     moveAnimation.duration = 2;
旋转

CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@ "transform.rotation.x" ];
     rotateAnimation.fromValue = [NSNumber numberWithFloat:0.0];
     rotateAnimation.toValue = [NSNumber numberWithFloat:6.0 * M_PI];
     rotateAnimation.autoreverses = YES;
     rotateAnimation.repeatCount = MAXFLOAT;
     rotateAnimation.duration = 2;
创建组动画
CAAnimationGroup *groupAnnimation = [CAAnimationGroup animation];
     groupAnnimation.duration = 2;
     groupAnnimation.autoreverses = YES;
     groupAnnimation.animations = @[moveAnimation, scaleAnimation, rotateAnimation];
     groupAnnimation.repeatCount = MAXFLOAT;
将组动画加入到layer上
[groupLayer addAnimation:groupAnnimation forKey:@"groupAnnimation”];

通过步骤基本就对核心动画有了大概了解,下面就开始从头详细说明了

一,clayer(我们的画纸)

主要属性有

@property CGRect bounds;
@property CGPoint position;
@property CGPoint anchorPoint;
@property CGColorRef backgroundColor;
@property CATransform3D transform;
我们通过最后一个属性其实可以直接完成一些简单的动画,但是其动画的时间可能比较迅速,有时候达不到我们要的效果。其实还有一种方法就是通过CADisplayLink 来事实现,我们将transform的尺度缩小,然后通过CADisplayLink可以缓慢的改变。CADisplayLink其实就是一个定时器,是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右。

// 定义
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotationChange)];
// 添加到主循环队列
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
// 暂停
link.paused = YES;
// 开始
link.paused = NO;
二,CAAnimation(我们的画笔)

CAAnimation是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类

基本属性说明:

duration:动画的持续时间

repeatCount:重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT

repeatDuration:重复时间

removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillModekCAFillModeForwards

fillMode:决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之

beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间

timingFunction:速度控制函数,控制动画运行的节奏

 

delegate:动画的代理

CAAnimation在分类中定义了代理方法

/* Called when the animation begins its active duration. */
// 动画开始时调用
- (void)animationDidStart:(CAAnimation *)anim;

/* Called when the animation either completes its active duration or
 * is removed from the object it is attached to (i.e. the layer). 'flag'
 * is true if the animation reached the end of its active duration
 * without being removed. */
// 动画结束后调用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

CALayer上动画的暂停和恢复

#pragma mark 暂停CALayer的动画
-(void)pauseLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];

    // 让CALayer的时间停止走动
      layer.speed = 0.0;
    // 让CALayer的时间停留在pausedTime这个时刻
    layer.timeOffset = pausedTime;
}

#pragma mark 恢复CALayer的动画
-(void)resumeLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = layer.timeOffset;
    // 1. 让CALayer的时间继续行走
      layer.speed = 1.0;
    // 2. 取消上次记录的停留时刻
      layer.timeOffset = 0.0;
    // 3. 取消上次设置的时间
      layer.beginTime = 0.0;
    // 4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    // 5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
      layer.beginTime = timeSincePause;
}

CAPropertyAnimation

是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation。

基本属性说明:

属性 说明
keyPath 通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@“position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果

CABasicAnimation——基本动画

属性说明:

属性 说明
fromValue keyPath相应属性的初始值
toValue keyPath相应属性的结束值

动画过程说明:

随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue。

keyPath内容是CALayer的可动画Animatable属性。

如果fillMode = kCAFillModeForwards同时removedOnComletion = NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。

CAKeyframeAnimation——关键帧动画

关键帧动画,也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:

  • CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
  • CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation

属性说明:

属性 说明
values NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path 可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略
keyTimes 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

CAAnimationGroup——动画组

动画组,是CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。

默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。

属性说明:

属性 说明
animations 用来保存一组动画对象的NSArray

CATransition——转场动画

CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。

UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。

属性说明:

属性 说明
type 动画过渡类型
subtype 动画过度方向
startProgress 动画起点(在整体动画的百分比)
endProgress 动画终点(在整体动画的百分比)

过渡效果设置


type

使用UIView动画函数实现转场动画——双视图

+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion;
参数 说明
duration 动画持续时间
option 动画类型
animations 将改变视图属性的代码放在这个block中
completion 动画结束后,会自动调用这个block

猜你喜欢

转载自blog.csdn.net/darrenzzb66/article/details/52150676