美文网首页
iOS CAAnimation动画初探

iOS CAAnimation动画初探

作者: 时间不会倒着走 | 来源:发表于2017-11-02 19:07 被阅读0次

    先看看CAAnimation动画的继承结构

    CAAnimation{

    CAPropertyAnimation {

    CABasicAnimation {

    CASpringAnimation

    }

    CAKeyframeAnimation

    }

    CATransition

    CAAnimationGroup

    }

    CAAnimation基本属性详解

    //动画的动作规则,包含以下值

    //kCAMediaTimingFunctionLinear 匀速

    //kCAMediaTimingFunctionEaseIn 慢进快出

    //kCAMediaTimingFunctionEaseOut 快进慢出

    //kCAMediaTimingFunctionEaseInEaseOut 慢进慢出 中间加速

    //kCAMediaTimingFunctionDefault 默认

    @property(nullable, strong) CAMediaTimingFunction *timingFunction;

    //动画的代理回调

    @property(nullable, strong) id delegate;

    //动画执行完以后是否移除动画,默认YES

    @property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;

    以上属性详解:

    delegate:动画执行的代理,在动画开始前设定,不用显式的写在代码里,它包含两个方法:

    动画开始回调

    - (void)animationDidStart:(CAAnimation *)aim;

    动画结束回调

    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

    removedOnCompletion:动画完成后是否移除动画.默认为YES.此属性为YES时, fillMode不可用,具体为什么不可用,可以自己结合两个属性分析一下,这里不再赘述.

    timingFunction 设置动画速度曲线,默认值上面已经给出.下面说它的几个方法:

    这两个方法是一样的.如果我们对系统自带的速度函数不满意,可以通过这两个函数创建一个自己喜欢的速度曲线函数,具体用法可以参考这篇文章CAMediaTimingFunction的使用

    + (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

    - (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

    获取曲线函数的缓冲点,具体用法可以参考这篇文章:iOS-核心动画高级编程/10-缓冲

    - (void)getControlPointAtIndex:(size_t)idx values:(float[2])ptr;

    CAAnimation协议的属性

    //开始时间.这个属性比较复杂,傻瓜用法为:CACurrentMediaTime() + x,

    //其中x为延迟时间.如果设置 beginTime = CACurrentMediaTime() + 1.0,产生的效果为延迟一秒执行动画,下面详解原理

    @property CFTimeInterval beginTime;

    //动画执行时间,此属性和speed有关系speed默认为1.0,如果speed设置为2.0,那么动画执行时间则为duration*(1.0/2.0).

    @property CFTimeInterval duration;

    //动画执行速度,它duration的关系参考上面解释

    @property float speed;

    //动画的时间延迟,这个属性比较复杂,下面详解

    @property CFTimeInterval timeOffset;

    //重复执行次数

    @property float repeatCount;

    //重复执行时间,此属性优先级大于repeatCount.也就是说如果repeatDuration设置为1秒重复10次,那么它会在1秒内执行完动画.

    @property CFTimeInterval repeatDuration;

    //是否自动翻转动画,默认NO.如果设置YES,那么整个动画的执行效果为A->B->A.

    @property BOOL autoreverses;

    //动画的填充方式,默认为: kCAFillModeRemoved,包含以下值

    //kCAFillModeForwards//动画结束后回到准备状态

    //kCAFillModeBackwards//动画结束后保持最后状态

    //kCAFillModeBoth//动画结束后回到准备状态,并保持最后状态

    //kCAFillModeRemoved//执行完成移除动画

    @property(copy) NSString *fillMode;

    以上属性的详解:

    beginTime:刚才上面简单解释了下这个属性的用法:CACurrentMediaTime()+ x 会使动画延迟执行x秒.不知道到这里有没有人想过如果-x会出现怎么样效果?假设我们有执行一个3秒的动画,然后设置beginTime =  CACurrentMediaTime()- 1.5那么执行动画你会发现动画只会执行后半段,也就是只执行后面的3-1.5s的动画.为什么会这样?其实动画都有一个timeline(时间线)的概念.动画开始执行都是基于这个时间线的绝对时间,这个时间和它的父类有关(系统的属性注释可以看到).默认的CALayer的beginTime为零,如果这个值为零的话,系统会把它设置为CACurrentMediaTime(),那么这个时间就是正常执行动画的时间:立即执行.所以如果你设置beginTime=CACurrentMediaTime()+x;它会把它的执行时间线推迟x秒,也就是晚执行x秒,如果你beginTime=CACurrentMediaTime()-x;那它开始的时候会从你动画对应的绝对时间开始执行.

    timeOffset:时间偏移量,默认为0;既然它是时间偏移量,那么它即和动画时间相关.这么解释:假设我们设置一个动画时间为5s,动画执行的过程为1->2->3->4->5,这时候如果你设置timeOffset = 2s那么它的执行过程就会变成3->4->5->1->2如果你设置timeOffset = 4s那么它的执行过程就会变成5->1->2->3->4,这么说应该很明白了吧?

    CAPropertyAnimation的基本属性

    //需要动画的属性值

    @property(nullable, copy) NSString *keyPath;

    //属性动画是否以当前动画效果为基础,默认为NO

    @property(getter=isAdditive) BOOL additive;

    //指定动画是否为累加效果,默认为NO

    @property(getter=isCumulative) BOOL cumulative;

    //此属性相当于CALayer中的transform属性,下面会详解

    @property(nullable, strong) CAValueFunction *valueFunction;

    以上属性的详解:

    CAPropertyAnimation是属性动画.顾名思义也就是针对属性才可以做的动画.那它可以对谁的属性可以做动画?是CALayer的属性,比如:bounds,position等.那么问题来了,我们改变CALayer的position可以直接设置[CAPropertyAnimation animationWithKeyPath:@"position"]如果我们设置它的transform(CATransform3D)呢?CATransform3D是一个矩阵,如果我们想为它做动画怎么办?下面这个属性就是用来解决这个问题的.

    valueFunction:我们来看它可以设置的值:

    kCAValueFunctionRotateX

    kCAValueFunctionRotateY

    kCAValueFunctionRotateZ

    kCAValueFunctionScale

    kCAValueFunctionScaleX

    kCAValueFunctionScaleY

    kCAValueFunctionScaleZ

    kCAValueFunctionTranslate

    kCAValueFunctionTranslateX

    kCAValueFunctionTranslateY

    kCAValueFunctionTranslateZ

    CAPropertyAnimation的方法

    //通过key创建一个CAPropertyAnimation对象

    + (instancetype)animationWithKeyPath:(nullable NSString *)path;

    下面我们来看一下可以设置属性动画的属性归总:

    CATransform3D{

    rotation旋转

    transform.rotation.x

    transform.rotation.y

    transform.rotation.z

    scale缩放

    transform.scale.x

    transform.scale.y

    transform.scale.z

    translation平移

    transform.translation.x

    transform.translation.y

    transform.translation.z

    }

    CGPoint{

    position

    position.x

    position.y

    }

    CGRect{

    bounds

    bounds.size

    bounds.size.width

    bounds.size.height

    bounds.origin

    bounds.origin.x

    bounds.origin.y

    }

    property{

    opacity

    backgroundColor

    cornerRadius

    borderWidth

    contents

    Shadow{

    shadowColor

    shadowOffset

    shadowOpacity

    shadowRadius

    }

    }

    总结:CAAnimation是基类,CAPropertyAnimation**是抽象类,两者都不可以直接使用, 那我们只有使用它的子类了.

    CABasicAnimation基本动画

    CABasicAnimation的属性

    //开始值

    @property(nullable, strong) id fromValue;

    //结束值

    @property(nullable, strong) id toValue;

    //结束值

    @property(nullable, strong) id byValue;

    这三个属性之间的规则

    fromValue和toValue不为空,动画的效果会从fromValue的值变化到toValue.

    fromValue和byValue都不为空,动画的效果将会从fromValue变化到fromValue+byValue

    toValue和byValue都不为空,动画的效果将会从toValue-byValue变化到toValue

    只有fromValue的值不为空,动画的效果将会从fromValue的值变化到当前的状态.

    只有toValue的值不为空,动画的效果将会从当前状态的值变化到toValue的值.

    只有byValue的值不为空,动画的效果将会从当前的值变化到(当前状态的值+byValue)的值.

    CABasicAnimation看起来不太复杂,但实际只用这个就足以可以做很多种动画了,下面简单用一下,先看效果:


    CABasicAnimation.gif

    实现代码:

    - (IBAction)animation:(id)sender

    {

    UIButton *btn = (UIButton *)sender;

    CABasicAnimation *animation = nil;

    switch (btn.tag)

    {

    //淡如淡出

    case 0:

    animation = [CABasicAnimation animationWithKeyPath:@"opacity"];

    [animation setFromValue:@1];

    [animation setToValue:@0.1];

    break;

    //缩放

    case 1:

    animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];

    [animation setFromValue:@1];

    [animation setToValue:@0.1];

    break;

    //旋转

    case 2:

    animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

    [animation setToValue:@(M_PI)];

    break;

    //平移

    case 3:

    animation = [CABasicAnimation animationWithKeyPath:@"position"];

    [animation setToValue:[NSValue valueWithCGPoint:CGPointMake(self.opView.center.x, self.opView.center.y+200)]];

    break;

    default:

    break;

    }

    animation.delegate = self;

    animation.duration = 0.3;//设置动画时间,单次动画时间

    animation.removedOnCompletion = NO;//默认为YES,设置为NO时setFillMode有效

    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

    //设置自动翻转

    //设置自动翻转以后单次动画时间不变,总动画时间增加一倍,它会让你前半部分的动画以相反的方式动画过来

    //比如说你设置执行一次动画,从a到b时间为1秒,设置自动翻转以后动画的执行方式为,先从a到b执行一秒,然后从b到a再执行一下动画结束

    [animation setAutoreverses:YES];

    //kCAFillModeForwards//动画结束后回到准备状态

    //kCAFillModeBackwards//动画结束后保持最后状态

    //kCAFillModeBoth//动画结束后回到准备状态,并保持最后状态

    //kCAFillModeRemoved//执行完成移除动画

    [animation setFillMode:kCAFillModeBoth];

    //将动画添加到layer,添加到图层开始执行动画,

    //注意:key值的设置与否会影响动画的效果

    //如果不设置key值每次执行都会创建一个动画,然后创建的动画会叠加在图层上

    //如果设置key值,系统执行这个动画时会先检查这个动画有没有被创建,如果没有的话就创建一个,如果有的话就重新从头开始执行这个动画

    //你可以通过key值获取或者删除一个动画:

    [self.opView.layer addAnimation:animation forKey:@"baseanimation"];

    }

    CASpringAnimation弹性动画

    CASpringAnimation的属性(iOS9新加)

    //理解下面的属性的时候可以结合现实物理现象,比如把它想象成一个弹簧上挂着一个金属小球

    //质量,振幅和质量成反比

    @property CGFloat mass;

    //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快

    @property CGFloat stiffness;

    //阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快,可以认为它是阻力系数

    @property CGFloat damping;

    //初始速率,动画视图的初始速度大小速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反.

    @property CGFloat initialVelocity;

    //结算时间,只读.返回弹簧动画到停止时的估算时间,根据当前的动画参数估算通常弹簧动画的时间使用结算时间比较准确

    @property(readonly) CFTimeInterval settlingDuration;

    下面我们写一个demo看看效果:


    实现代码:

    - (IBAction)animationBegin:(id)sender

    {

    UIButton *btn = (UIButton *)sender;

    CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"position.y"];

    springAnimation.mass = 1;

    springAnimation.stiffness = 100;

    springAnimation.damping = 1;

    springAnimation.initialVelocity = 0;

    springAnimation.duration = springAnimation.settlingDuration;

    springAnimation.fromValue = @(self.opView.center.y);

    springAnimation.toValue = @(self.opView.center.y + (btn.selected?+150:-150));

    springAnimation.fillMode = kCAFillModeForwards;

    [self.opView.layer addAnimation:springAnimation forKey:nil];

    btn.selected = !btn.selected;

    }

    CAKeyframeAnimation关键帧动画

    CAKeyframeAnimation的属性

    //关键帧值数组,一组变化值

    @property(nullable, copy) NSArray *values;

    //关键帧帧路径,优先级比values大

    @property(nullable) CGPathRef path;

    //每一帧对应的时间,时间可以控制速度.它和每一个帧相对应,取值为0.0-1.0,不设则每一帧时间相等.

    @property(nullable, copy) NSArray *keyTimes;

    //每一帧对应的时间曲线函数,也就是每一帧的运动节奏

    @property(nullable, copy) NSArray *timingFunctions;

    //动画的计算模式,默认值: kCAAnimationLinear.有以下几个值:

    //kCAAnimationLinear//关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;

    //kCAAnimationDiscrete//离散的,也就是没有补间动画

    //kCAAnimationPaced//平均,keyTimes跟timeFunctions失效

    //kCAAnimationCubic对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,keyTimes跟timeFunctions失效

    //kCAAnimationCubicPaced在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,,keyTimes跟timeFunctions失效

    @property(copy) NSString *calculationMode;

    //动画的张力,当动画为立方计算模式的时候此属性提供了控制插值,因为每个关键帧都可能有张力所以连续性会有所偏差它的范围为[-1,1].同样是此作用

    @property(nullable, copy) NSArray *tensionValues;

    //动画的连续性值

    @property(nullable, copy) NSArray *continuityValues;

    //动画的偏斜率

    @property(nullable, copy) NSArray *biasValues;

    //动画沿路径旋转方式,默认为nil.它有两个值:

    //kCAAnimationRotateAuto//自动旋转,

    //kCAAnimationRotateAutoReverse//自动翻转

    @property(nullable, copy) NSString *rotationMode;

    CAKeyframeAnimation可以做很丰富的效果,下面展示了几种纯CAKeyframeAnimation做的效果:


    实现代码:

    - (IBAction)animationBegin:(id)sender {

    UIButton *btn = (UIButton *)sender;

    switch (btn.tag) {

    case 0:

    case 1:

    case 2:

    case 3:

    case 4:

    case 5:

    [self path:btn.tag];break;

    case 6:

    case 7:

    [self values:btn.tag];break;

    default:

    break;

    }

    }

    -(void)path:(NSInteger)tag{

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    switch (tag) {

    case 0:{

    //椭圆

    CGMutablePathRef path = CGPathCreateMutable();//创建可变路径

    CGPathAddEllipseInRect(path, NULL, CGRectMake(0, 0, 320, 500));

    [animation setPath:path];

    CGPathRelease(path);

    animation.rotationMode = kCAAnimationRotateAuto;

    }break;

    case 1:{

    //贝塞尔,矩形

    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 320, 320)];

    //animation需要的类型是CGPathRef,UIBezierPath是ui的,需要转化成CGPathRef

    [animation setPath:path.CGPath];

    }break;

    case 2:{

    //贝塞尔,抛物线

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:self.opView.center];

    [path addQuadCurveToPoint:CGPointMake(0, 568)

    controlPoint:CGPointMake(400, 100)];

    [animation setPath:path.CGPath];

    }break;

    case 3:{

    //贝塞尔,s形曲线

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointZero];

    [path addCurveToPoint:self.opView.center

    controlPoint1:CGPointMake(320, 100)

    controlPoint2:CGPointMake(  0, 400)];

    ;

    [animation setPath:path.CGPath];

    }break;

    case 4:{

    //贝塞尔,圆形

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:self.view.center

    radius:150

    startAngle:- M_PI * 0.5

    endAngle:M_PI * 2

    clockwise:YES];

    [animation setPath:path.CGPath];

    }break;

    case 5:{

    CGPoint point = CGPointMake(self.view.center.x, 400);

    CGFloat xlength = point.x - self.opView.center.x;

    CGFloat ylength = point.y - self.opView.center.y;

    CGMutablePathRef path = CGPathCreateMutable();

    //移动到目标点

    CGPathMoveToPoint(path, NULL, self.opView.center.x, self.opView.center.y);

    //将目标点的坐标添加到路径中

    CGPathAddLineToPoint(path, NULL, point.x, point.y);

    //设置弹力因子,

    CGFloat offsetDivider = 5.0f;

    BOOL stopBounciong = NO;

    while (stopBounciong == NO) {

    CGPathAddLineToPoint(path, NULL, point.x + xlength / offsetDivider, point.y + ylength / offsetDivider);

    CGPathAddLineToPoint(path, NULL, point.x, point.y);

    offsetDivider += 6.0;

    //当视图的当前位置距离目标点足够小我们就退出循环

    if ((ABS(xlength / offsetDivider) < 10.0f) && (ABS(ylength / offsetDivider) < 10.0f)) {

    break;

    }

    }

    [animation setPath:path];

    }break;

    default:break;

    }

    [animation setDuration:0.5];

    [animation setRemovedOnCompletion:NO];

    [animation setFillMode:kCAFillModeBoth];

    [self.opView.layer addAnimation:animation forKey:nil];

    }

    -(void)values:(NSInteger)tag{

    CAKeyframeAnimation *animation = nil;

    switch (tag) {

    case 6:{

    animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];

    CGFloat angle = M_PI_4 * 0.5;

    NSArray *values = @[@(angle),@(-angle),@(angle)];

    [animation setValues:values];

    [animation setRepeatCount:3];

    [animation setDuration:0.5];

    }break;

    case 7:{

    animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    NSValue *p1 = [NSValue valueWithCGPoint:self.opView.center];

    NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(self.view.center.x + 100, 200)];

    NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(self.view.center.x, 300)];

    //设置关键帧的值

    [animation setValues:@[p1,p2,p3]];

    [animation setDuration:0.5];

    }break;

    default:break;

    }

    UIGraphicsBeginImageContext(self.view.frame.size);

    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    [animation setRemovedOnCompletion:NO];

    [animation setFillMode:kCAFillModeBoth];

    [self.opView.layer addAnimation:animation forKey:nil];

    }

    CAAnimationGroup动画组

    CAAnimationGroup的属性

    //只有一个属性,数组中接受CAAnimation元素

    @property(nullable, copy) NSArray *animations;

    可以看到CAAnimationGroup只有一个属性一个CAAnimation数组.而且它继承于CAAnimation,它具有CAAnimation的特性,所以它的用法和CAAnimation是一样的,不同的是他可以包含n个动画,也就是说他可以接受很多个CAAnimation并且可以让它们一起开始,这就造成了动画效果的叠加,效果就是n个动画同时进行.

    来看一个简单的效果:


    实现代码:

    - (IBAction)animationBegin:(id)sender

    {

    CAAnimationGroup *group = [CAAnimationGroup animation];

    /**

    *  移动动画

    */

    CAKeyframeAnimation *position = [self moveAnimation];

    /**

    *  摇晃动画

    */

    CAKeyframeAnimation *shake = [self shakeAnimation];

    /**

    *  透明度动画

    */

    CABasicAnimation *alpha = [self alphaAnimation];

    /**

    *  设置动画组的时间,这个时间表示动画组的总时间,它的子动画的时间和这个时间没有关系

    */

    [group setDuration:3.0];

    [group setAnimations:@[position,shake,alpha]];

    [self.opView.layer addAnimation:group forKey:nil];

    }

    #pragma mark -- CAKeyframeAnimation - 路径平移动画

    -(CAKeyframeAnimation *)moveAnimation

    {

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    /**

    *  设置路径,按圆运动

    */

    CGMutablePathRef path = CGPathCreateMutable();//CG是C语言的框架,需要直接写语法

    CGPathAddEllipseInRect(path, NULL, CGRectMake(0, 0, 320, 320));

    [animation setPath:path];//把路径给动画

    CGPathRelease(path);//释放路径

    /**

    *  设置动画时间,这是动画总时间,不是每一帧的时间

    */

    [animation setDuration:3];

    /**

    *setRemovedOnCompletion 设置动画完成后是否将图层移除掉,默认是移除

    *setFillMode 当前设置的是向前填充,意味着动画完成后填充效果为最新的效果,此属性有效的前提是 setRemovedOnCompletion=NO

    *注意:

    *1.动画只是改变人的视觉,它并不会改变视图的初始位置等信息,也就是说无论动画怎么东,都不会改变view的原始大小,只是看起来像是大小改变了而已

    *2.因为没有改变视图的根本大小,所以视图所接收事件的位置还是原来的大小,可以不是显示的大小

    */

    [animation setRemovedOnCompletion:NO];

    [animation setFillMode:kCAFillModeForwards];

    return animation;

    }

    #pragma mark -- CAKeyframeAnimation - 摇晃动画

    -(CAKeyframeAnimation *)shakeAnimation

    {

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];

    /**

    *  设置路径,贝塞尔路径

    */

    CGFloat angle = M_PI_4 * 0.1;

    NSArray *values = @[@(angle),@(-angle),@(angle)];

    [animation setValues:values];

    [animation setRepeatCount:10];

    /**

    *  设置动画时间,这是动画总时间,不是每一帧的时间

    */

    [animation setDuration:0.25];

    return animation;

    }

    #pragma mark -- CABasicAnimation - 淡如淡出动画

    -(CABasicAnimation *)alphaAnimation

    {

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];

    [animation setDuration:1.0];

    /**

    * 设置重复次数

    */

    [animation setRepeatCount:3];

    /**

    * 设置自动翻转

    * 设置自动翻转以后单次动画时间不变,总动画时间延迟一倍,它会让你前半部分的动画以相反的方式动画过来

    * 比如说你设置执行一次动画,从a到b时间为1秒,设置自动翻转以后动画的执行方式为,先从a到b执行一秒,然后从b到a再执行一下动画结束

    */

    [animation setAutoreverses:YES];

    /**

    * 设置起始值

    */

    [animation setFromValue:@1.0];

    /**

    * 设置目标值

    */

    [animation setToValue:@0.1];

    /**

    * 将动画添加到layer 添加到图层开始执行动画,

    * 注意:key值的设置与否会影响动画的效果

    * 如果不设置key值每次执行都会创建一个动画,然后创建的动画会叠加在图层上

    * 如果设置key值,系统执行这个动画时会先检查这个动画有没有被创建,如果没有的话就创建一个,如果有的话就重新从头开始执行这个动画

    * 你可以通过key值获取或者删除一个动画

    */

    return animation;

    }

    CATransition转场动画

    CATransition属性

    //转场类型,字符串类型参数.系统提供了四中动画形式:

    //kCATransitionFade//逐渐消失

    //kCATransitionMoveIn//移进来

    //kCATransitionPush//推进来

    //kCATransitionReveal//揭开

    //另外,除了系统给的这几种动画效果,我们还可以使用系统私有的动画效果:

    //@"cube",//立方体翻转效果

    //@"oglFlip",//翻转效果

    //@"suckEffect",//收缩效果,动画方向不可控

    //@"rippleEffect",//水滴波纹效果,动画方向不可控

    //@"pageCurl",//向上翻页效果

    //@"pageUnCurl",//向下翻页效果

    //@"cameralIrisHollowOpen",//摄像头打开效果,动画方向不可控

    //@"cameraIrisHollowClose",//摄像头关闭效果,动画方向不可控

    @property(copy) NSString *type;

    //转场方向,系统一共提供四个方向:

    //kCATransitionFromRight//从右开始

    //kCATransitionFromLeft//从左开始

    //kCATransitionFromTop//从上开始

    //kCATransitionFromBottom//从下开始

    @property(nullable, copy) NSString *subtype;

    //开始进度,默认0.0.如果设置0.3,那么动画将从动画的0.3的部分开始

    @property float startProgress;

    //结束进度,默认1.0.如果设置0.6,那么动画将从动画的0.6部分以后就会结束

    @property float endProgress;

    //开始进度

    @property(nullable, strong) id filter;

    CATransition也是继承CAAnimation,系统默认提供了12种动画样式,加上4个动画方向,除了方向不可控的四种效果外,大概一共提供了36种动画.

    另外系统还给UIView添加了很多分类方法可以快速完成一些简单的动画,如下:

    UIView(UIViewAnimation)

    @interface UIView(UIViewAnimation)

    + (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;  // additional context info passed to will start/did stop selectors. begin/commit can be nested

    //提交动画

    + (void)commitAnimations;

    //设置代理

    + (void)setAnimationDelegate:(nullable id)delegate;                          //设置动画开始方法

    + (void)setAnimationWillStartSelector:(nullable SEL)selector;

    //设置动画结束方法

    + (void)setAnimationDidStopSelector:(nullable SEL)selector;

    //设置动画时间:default = 0.2

    + (void)setAnimationDuration:(NSTimeInterval)duration;

    //设置动画延迟开始时间:default = 0.0

    + (void)setAnimationDelay:(NSTimeInterval)delay;

    //设置动画延迟开始日期:default = now ([NSDate date])

    + (void)setAnimationStartDate:(NSDate *)startDate;

    //设置动画运动曲线:default =UIViewAnimationCurveEaseInOut

    //UIViewAnimationCurveEaseInOut,//慢进慢出

    //UIViewAnimationCurveEaseIn, //慢进快出

    //UIViewAnimationCurveEaseOut,//快进慢出

    //UIViewAnimationCurveLinear//匀速

    + (void)setAnimationCurve:(UIViewAnimationCurve)curve;

    //设置重复次数: default = 0.0.  May be fractional

    + (void)setAnimationRepeatCount:(float)repeatCount;

    //设置是否翻转动画: default = NO. used if repeat

    + (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;

    //设置动画是否从当前状态开始:default = NO

    + (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;

    //设置动画类型

    + (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;

    //设置动画是否有效

    + (void)setAnimationsEnabled:(BOOL)enabled;

    //

    + (BOOL)areAnimationsEnabled;

    //

    + (void)performWithoutAnimation:(void (^)(void))actionsWithoutAnimation

    //

    + (NSTimeInterval)inheritedAnimationDuration

    @end

    UIView(UIViewAnimationWithBlocks)

    @interface UIView(UIViewAnimationWithBlocks)

    //以下方法都大同小异,就不一一做注释了

    + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

    + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion

    + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;

    + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion

    + (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

    + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion;

    + (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

    @end

    UIView (UIViewKeyframeAnimations)

    + (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;

    + (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations

    以上方法比较多,找值得说的简单说一下吧:

    //单视图转场动画

    + (void)transitionWithView:(UIView *)view

    duration:(NSTimeInterval)duration

    options:(UIViewAnimationOptions)options

    animations:(void (^ __nullable)(void))animations

    completion:(void (^ __nullable)(BOOL finished))completion

    //双视图转场动画

    + (void)transitionFromView:(UIView *)fromView

    toView:(UIView *)toView

    duration:(NSTimeInterval)duration

    options:(UIViewAnimationOptions)options

    completion:(void (^ __nullable)(BOOL finished))completion

    这两个都是转场动画,不同的是第一个是单视图转场,第二个是双视图转场.不过需要注意的是:单视图转场动画只能用作属性动画做不到的转场效果,比如属性动画不能给UIImageview的image赋值操作做动画效果等.

    我们可以看到以上两个方法中都有一个共同的参数:

    UIViewAnimationOptions

    typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {

    UIViewAnimationOptionLayoutSubviews            = 1 <<  0,

    UIViewAnimationOptionAllowUserInteraction      = 1 <<  1, // turn on user interaction while animating

    UIViewAnimationOptionBeginFromCurrentState    = 1 <<  2, // start all views from current value, not initial value

    UIViewAnimationOptionRepeat                    = 1 <<  3, // repeat animation indefinitely

    UIViewAnimationOptionAutoreverse              = 1 <<  4, // if repeat, run animation back and forth

    UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5, // ignore nested duration

    UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6, // ignore nested curve

    UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7, // animate contents (applies to transitions only)

    UIViewAnimationOptionShowHideTransitionViews  = 1 <<  8, // flip to/from hidden state instead of adding/removing

    UIViewAnimationOptionOverrideInheritedOptions  = 1 <<  9, // do not inherit any options or animation type

    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default

    UIViewAnimationOptionCurveEaseIn              = 1 << 16,

    UIViewAnimationOptionCurveEaseOut              = 2 << 16,

    UIViewAnimationOptionCurveLinear              = 3 << 16,

    UIViewAnimationOptionTransitionNone            = 0 << 20, // default

    UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,

    UIViewAnimationOptionTransitionFlipFromRight  = 2 << 20,

    UIViewAnimationOptionTransitionCurlUp          = 3 << 20,

    UIViewAnimationOptionTransitionCurlDown        = 4 << 20,

    UIViewAnimationOptionTransitionCrossDissolve  = 5 << 20,

    UIViewAnimationOptionTransitionFlipFromTop    = 6 << 20,

    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,

    } NS_ENUM_AVAILABLE_IOS(4_0);

    可以看到系统给到的是一个位移枚举,这就意味着这个枚举可以多个值同时使用,但是怎么用呢?其实那些枚举值可以分为三个部分.

    我们分别看一下每个枚举的意思:

    第一部分:动画效果

    UIViewAnimationOptionTransitionNone//没有效果

    UIViewAnimationOptionTransitionFlipFromLeft//从左水平翻转

    UIViewAnimationOptionTransitionFlipFromRight//从右水平翻转

    UIViewAnimationOptionTransitionCurlUp//翻书上掀

    UIViewAnimationOptionTransitionCurlDown//翻书下盖UIViewAnimationOptionTransitionCrossDissolve//融合

    UIViewAnimationOptionTransitionFlipFromTop//从上垂直翻转

    UIViewAnimationOptionTransitionFlipFromBottom//从下垂直翻转

    第二部分:动画运动曲线

    //开始慢,加速到中间,然后减慢到结束

    UIViewAnimationOptionCurveEaseInOut

    //开始慢,加速到结束

    UIViewAnimationOptionCurveEaseIn

    //开始快,减速到结束

    UIViewAnimationOptionCurveEaseOut

    //线性运动

    UIViewAnimationOptionCurveLinear

    第三部分:其他

    //默认,跟父类作为一个整体

    UIViewAnimationOptionLayoutSubviews

    //设置了这个,主线程可以接收点击事件

    UIViewAnimationOptionAllowUserInteraction

    //从当前状态开始动画,父层动画运动期间,开始子层动画.

    UIViewAnimationOptionBeginFromCurrentState

    //重复执行动画,从开始到结束, 结束后直接跳到开始态

    UIViewAnimationOptionRepeat

    //反向执行动画,结束后会再从结束态->开始态

    UIViewAnimationOptionAutoreverse

    //忽略继承自父层持续时间,使用自己持续时间(如果存在)

    UIViewAnimationOptionOverrideInheritedDuration

    //忽略继承自父层的线性效果,使用自己的线性效果(如果存在)

    UIViewAnimationOptionOverrideInheritedCurve

    //允许同一个view的多个动画同时进行

    UIViewAnimationOptionAllowAnimatedContent

    //视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)            UIViewAnimationOptionShowHideTransitionViews

    //不继承父动画设置或动画类型.

    UIViewAnimationOptionOverrideInheritedOptions

    这下可以看到,这些枚举功能都不一样但是可以随意组合,但是组合的时候需要注意,同一类型的枚举不能一起使用比如UIViewAnimationOptionCurveEaseIn和UIViewAnimationOptionCurveEaseOut

    然后我们看一下转场动画的一些效果:

    单视图转场


    单视图转场.gif

    双视图转场


    双视图转场.gif

    CATransition转场


    CATransition转场.gif

    实现代码:

    - (IBAction)animationBegin:(id)sender

    {

    UIButton *btn = (UIButton *)sender;

    switch (btn.tag)

    {

    case 0:

    [self animationSingleView:YES];

    break;

    case 1:

    [self animationSingleView:NO];

    break;

    case 2:[self chang3];

    break;

    default:break;

    }

    }

    /**

    *  转场动画在执行过程中不可以被停止,

    *  转场动画在执行过程中不可以用户交互

    *  转场动画在执行过程中不可以控制动画执行进度

    */

    /**

    *  基于UIView的单视图转场动画

    */

    static NSUInteger change1_0Index = 0;

    static NSUInteger change1_1Index = 0;

    static NSUInteger change1_2Index = 0;

    -(void)animationSingleView:(BOOL)sigle

    {

    /**

    *  第一部分

    */

    NSArray *array0 = @[

    @(UIViewAnimationOptionTransitionNone),

    @(UIViewAnimationOptionTransitionFlipFromLeft),//从左水平翻转

    @(UIViewAnimationOptionTransitionFlipFromRight),//从右水平翻转

    @(UIViewAnimationOptionTransitionCurlUp),//翻书上掀

    @(UIViewAnimationOptionTransitionCurlDown),//翻书下盖

    @(UIViewAnimationOptionTransitionCrossDissolve),//融合

    @(UIViewAnimationOptionTransitionFlipFromTop),//从上垂直翻转

    @(UIViewAnimationOptionTransitionFlipFromBottom),//从下垂直翻转

    ];

    /**

    *  第二部分

    */

    NSArray *array1 = @[

    @(UIViewAnimationOptionCurveEaseInOut),////开始慢,加速到中间,然后减慢到结束

    @(UIViewAnimationOptionCurveEaseIn),//开始慢,加速到结束

    @(UIViewAnimationOptionCurveEaseOut),//开始快,减速到结束

    @(UIViewAnimationOptionCurveLinear),//线性运动

    ];

    /**

    *  第三部分

    */

    NSArray *array2 = @[

    @(UIViewAnimationOptionLayoutSubviews),//默认,跟父类作为一个整体

    @(UIViewAnimationOptionAllowUserInteraction),//设置了这个,主线程可以接收点击事件

    @(UIViewAnimationOptionBeginFromCurrentState),//从当前状态开始动画,父层动画运动期间,开始子层动画。

    @(UIViewAnimationOptionRepeat),//重复执行动画,从开始到结束, 结束后直接跳到开始态

    @(UIViewAnimationOptionAutoreverse),//反向执行动画,结束后会再从结束态->开始态

    @(UIViewAnimationOptionOverrideInheritedDuration),//忽略继承自父层持续时间,使用自己持续时间(如果存在)

    @(UIViewAnimationOptionOverrideInheritedCurve),//忽略继承自父层的线性效果,使用自己的线性效果(如果存在)

    @(UIViewAnimationOptionAllowAnimatedContent),//允许同一个view的多个动画同时进行

    @(UIViewAnimationOptionShowHideTransitionViews),//视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)

    @(UIViewAnimationOptionOverrideInheritedOptions),//不继承父动画设置或动画类型。

    ];

    //    CASpringAnimation

    //    CASpringAnimation

    if (sigle)

    {

    [UIView transitionWithView:self.opView1

    duration:1

    options:

    ((NSNumber *)array0[change1_0Index]).integerValue|

    ((NSNumber *)array1[change1_1Index]).integerValue|

    ((NSNumber *)array2[change1_2Index]).integerValue

    animations:^{

    /**

    *  单视图的转场动画需要在动画块中设置视图转场前的内容和视图转场后的内容

    */

    if (self.opView1.tag == 0)

    {

    self.opView1.image = [UIImage imageNamed:@"IMG_1730"];

    self.opView1.tag = 1;

    }

    else

    {

    self.opView1.image = [UIImage imageNamed:@"93F5E460-9D31-4780-8511-37FF91033402"];

    self.opView1.tag = 0;

    }

    } completion:nil];

    NSLog(@"动画:%s:%@:%@:%@",__func__,@(change1_0Index),@(change1_1Index),@(change1_2Index));

    }

    else

    {

    /**

    *  双视图的转场动画

    *  注意:双视图的转场动画实际上是操作视图移除和添加到父视图的一个过程,from视图必须要有父视图,to视图必须不能有父视图,否则会出问题

    *  比如动画不准等

    */

    UIImageView *fromView = nil;

    UIImageView *toView = nil;

    if (self.opView1.tag == 0)

    {

    fromView = self.opView1;

    toView = self.opView2;

    self.opView1.tag = 1;

    }

    else

    {

    fromView = self.opView2;

    toView = self.opView1;

    self.opView1.tag = 0;

    }

    [UIView transitionFromView:fromView

    toView:toView duration:1.0

    options:

    ((NSNumber *)array0[change1_0Index]).integerValue|

    ((NSNumber *)array1[change1_1Index]).integerValue|

    ((NSNumber *)array2[change1_2Index]).integerValue

    completion:^(BOOL finished) {

    [fromView removeFromSuperview];

    }];

    [self.view addSubview:toView];

    }

    change1_0Index += 1;

    if (change1_0Index > array0.count - 1)

    {

    change1_0Index = 0;

    change1_1Index += 1;

    }

    if (change1_1Index > array1.count - 1)

    {

    change1_1Index = 0;

    change1_2Index += 1;

    }

    if (change1_2Index > array2.count - 1)

    {

    change1_2Index = 0;

    change1_0Index = 0;

    change1_2Index = 0;

    }

    }

    /**

    *  基于CATransition的视图转场动画

    */

    static NSUInteger change3_0Index = 0;

    static NSUInteger change3_1Index = 0;

    -(void)chang3

    {

    /**

    *创建转场动画:注意:CATransaction和CATransition 不一样

    */

    CATransition *transition = [CATransition animation];

    transition.duration = 0.25;

    NSArray *type_array = @[

    //系统提供的动画

    kCATransitionFade,

    kCATransitionMoveIn,

    kCATransitionPush,

    kCATransitionReveal,

    //以下是私有api,只能字符串访问

    @"cube",//立方体翻转效果

    @"oglFlip",//翻转效果

    @"suckEffect",//收缩效果,动画方向不可控

    @"rippleEffect",//水滴波纹效果,动画方向不可控

    @"pageCurl",//向上翻页效果

    @"pageUnCurl",//向下翻页效果

    @"cameralIrisHollowOpen",//摄像头打开效果,动画方向不可控

    @"cameraIrisHollowClose",//摄像头关闭效果,动画方向不可控

    ];

    //转场类型

    transition.type = type_array[change3_0Index];

    NSArray *subtype_array = @[

    kCATransitionFromRight,

    kCATransitionFromLeft,

    kCATransitionFromTop,

    kCATransitionFromBottom

    ];

    //转场方向

    transition.subtype = subtype_array[change3_1Index];

    /**

    *  设置转场动画的开始和结束百分比

    */

    transition.startProgress = 0.0;

    transition.endProgress = 1.0;

    if (self.opView1.tag == 0)

    {

    self.opView1.tag = 1;

    self.opView1.image = [UIImage imageNamed:@"IMG_1730"];

    self.opView2.image = [UIImage imageNamed:@"93F5E460-9D31-4780-8511-37FF91033402"];

    }

    else

    {

    self.opView1.tag = 0;

    self.opView1.image = [UIImage imageNamed:@"93F5E460-9D31-4780-8511-37FF91033402"];

    self.opView2.image = [UIImage imageNamed:@"IMG_1730"];

    }

    [self.opView1.layer addAnimation:transition forKey:nil];

    [self.opView2.layer addAnimation:transition forKey:nil];

    NSLog(@"动画:%s:%@:%@",__func__,@(change3_0Index),@(change3_1Index));

    change3_1Index += 1;

    if (change3_1Index > subtype_array.count - 1)

    {

    change3_1Index = 0;

    change3_0Index += 1;

    }

    if (change3_0Index > type_array.count - 1)

    {

    change3_0Index = 0;

    }

    }

    作者:xqqlv

    链接:http://www.jianshu.com/p/4c3bace804e3

    來源:简书

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

          本文标题:iOS CAAnimation动画初探

          本文链接:https://www.haomeiwen.com/subject/teeapxtx.html