美文网首页
重拾CAAnimation

重拾CAAnimation

作者: wesk痕 | 来源:发表于2018-08-06 10:16 被阅读0次

看到工作很多年的同事,对于app发生的一些异常现象,不够重视,也没有很准确的分析到原因,为什么自己写的动画在某种场景下停下来了,而且是属于非常偶现的, 有些类的delloc没有调用,特此再过一遍基础,学而则精.

CAAnimation 动画抽象超类

@interface CAAnimation : NSObject
    <NSSecureCoding, NSCopying, CAMediaTiming, CAAction>

从上面看到 CAAnimations是一个抽象超类,遵循了CAMediaTimingCAAction协议,可以设置动画的开始时间、持续时间、速度、时间偏移、重复次数等

CAAnimation类图

CAAnimation的一些派生类:

  • CAPropertyAnimation: 支持动画地显示图层的keyPath,一般不直接使用。
  • CATransition: 提供渐变效果:(推拉push效果,消退fade效果,揭开reveal效果)
  • CAAnimationGroup: 允许多个动画同时播放
  • CABasicAnimation: 提供了对单一动画的实现
  • CAKeyframeAnimation: 关键桢动画,可以定义行动路线
  • CASpringAnimation: 实现弹簧效果的动画

比较常用的是CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup和CASpringAnimation;

协议——CAMediaTiming
  • beginTime: 动画的开始时, 默认为0

  • duration: 动画持续时间,默认为0

  • speed: 动画执行速度

  • timeOffset: 时间偏移量,默认为0

  • repeatCount: 重复次数,默认为0

  • repeatDuration: 重复间隔,默认为0

  • autoreverses: 动画自动逆向执行,默认为No

  • fillMode: 决定当前对象在应用程序非active时间段的行为。fillMode取值fillMode属性值

    kCAFillModeRemoved  这个是默认值,也就是说当动画开始前和动画结束后,
    动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
    kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
    kCAFillModeBackwards  在动画开始前,只需要将动画加入了一个layer,
    layer便立即进入动画的初始状态并等待动画开始。
    kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,
    layer便处于动画初始状态,动画结束后layer保持动画最后的状态
    
协议-CAAction
- (void)runActionForKey:(NSString *)event object:(id)anObject
    arguments:(nullable NSDictionary *)dict;
协议-NSCopying
协议-NSSecureCoding 数据归档编码解码
timeFunction 速度控制函数,控制动画运行的节奏
kCAMediaTimingFunctionLinear:线性动画 
kCAMediaTimingFunctionEaseIn:动画缓慢开始,然后逐渐加速 
kCAMediaTimingFunctionEaseOut:动画迅速开始,在结束时减速 
kCAMediaTimingFunctionEaseInEaseOut:动画慢慢开始,然后加速,在结束之前减速 
kCAMediaTimingFunctionDefault :系统默认动画效果
delegate 动画代理
animationDidStart: 动画开始时调用 
animationDidStop:finished: 动画结束后调用 
removedOnCompletion
/* When true, the animation is removed from the render tree once its **active** duration has passed. Defaults to YES. */
    
如果是true,一旦离开了活跃的动画时间,动画将从渲染树中移除。 

delegate和removedOnCompletion

CATransition 转场动画

  • type: 动画类型,有两种表达方式,字符串或系统宏,如:@"push"/kCATransitionPush

    kCATransitionFade
    kCATransitionMoveIn
    kCATransitionPush
    kCATransitionReveal
    fade:交叉淡化过渡
    push:新视图把旧视图推出去
    moveIn:新视图移到旧视图上面
    reveal:将旧视图移开,显示下面的新视图
    oglFlip:上下左右翻转效果
    cube:立方体翻滚效果
    suckEffect:收缩效果
    rippleEffect:水滴效果
    pageCurl:向上翻页效果
    pageUnCurl:向下翻页效果
    cameraIrisHollowOpen:相机镜头打开效果
    cameraIrisHollowClose:相机镜头关闭效果
    
  • subtype: 指定动画的方向

    kCATransitionFromRight
    kCATransitionFromLeft
    kCATransitionFromTop 
    kCATransitionFromBottom 
    
  • startProgress: 定义动画的开始点 范围0-1

  • endProgress:定义动画的结束点 值必须大于结束点 范围0-1

CATransition

动画实现:

    CATransition *anim = [CATransition animation];
    // 设置转场类型
    anim.type = @"cube";
    // 设置动画的方向
    anim.subtype = kCATransitionFromLeft;
    anim.startProgress = 0.1;
    anim.endProgress = 0.5;
    anim.removedOnCompletion = NO;
    anim.fillMode = kCAFillModeBoth;
    anim.duration = 0.3;
    [self.tranImageView.layer addAnimation:anim forKey:@"transition"];

CAPropertyAnimation

CAPropertyAnimation也是一个抽象类,不能使用该类直接来完成动画!

keyPath 指定接收层动画的关键路径 如:transform.rotation.x(绕着x轴旋转)
cumulative 下一次动画执行是否接着刚才的动画,默认为false
additive 如何处理多个动画在同一时间段执行的结果,若为true,同一时间段的动画合成为一个动画,默认为false。(使用 CAKeyframeAnimation 时必须将该属性指定为 true ,否则不会出现期待的结果)

keyPath : Animatable

CABasicAnimation 基础动画

  • fromValue:keyPath相应属性的初始值
  • byValue:keyPaht相应属性的相对插值
  • toValue:keyPath相应属性的结束值

圆圈进度动画 如实现下载进度实现


CABasicAnimation

动画实现:

- (void)drawCircleAn![2018-08-06 09_42_58.gif](https://img.haomeiwen.com/i2906485/2408ed4082c5ce72.gif?imageMogr2/auto-orient/strip)
![![![2018-08-06 09_42_58.gif](https://img.haomeiwen.com/i2906485/2ac66989f36d647d.gif?imageMogr2/auto-orient/strip)
](https://img.haomeiwen.com/i2906485/dec574af6248c58d.gif?imageMogr2/auto-orient/strip)
](https://img.haomeiwen.com/i2906485/9c211991cb638631.gif?imageMogr2/auto-orient/strip)

{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    animation.fromValue = @0;
    animation.toValue = @1;
    animation.duration = 3;
    CAShapeLayer *shapeLayer = [self drawCircle];
    [shapeLayer addAnimation:animation forKey:@"circleAnimation"];
    // 将CAShaperLayer放到View上显示
    [self.view.layer addSublayer:shapeLayer];
}

- (CAShapeLayer *)drawCircle {
    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    // 指定frame,只是为了设置宽度和高度
    circleLayer.frame = CGRectMake(0, 0, 100, 100);
    // 设置居中显示
    circleLayer.position = self.view.center;
    // 设置填充颜色
    circleLayer.fillColor = [UIColor clearColor].CGColor;
    // 设置线宽
    circleLayer.lineWidth = 4.0;
    // 设置线的颜色
    circleLayer.strokeColor = [UIColor purpleColor].CGColor;
    circleLayer.fillColor = [UIColor whiteColor].CGColor;
    // 使用UIBezierPath创建路径
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 0) radius:50 startAngle:-M_PI_2 endAngle:1.5*M_PI clockwise:YES];;
    // 设置CAShapeLayer与UIBezierPath关联
    circleLayer.path = circlePath.CGPath;
    return circleLayer;
}

CAKeyframeAnimation 关键帧动画

  • values:关键帧动画值的数组,当path为nil时设置有效,否则优先选择属性path做动画
  • path:动画执行的点路径(通过Core Graphics提供的API来绘制路径),设置了path,values将被忽略
  • keyTimes:关键帧动画每帧动画开始执行时间点的数组,取值范围为0~1,数组中相邻两个值必须遵循后一个值大于或等于前一个值,并且最后的值不能为大于1。设置的时候与calculationMode有关. (n-1)个CAMediaTimingFunction.
  • timingFunctions:动画执行效果数组
kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。
kCAMediaTimingFunctionDefault(默认时间函数)
  • calculationMode:关键帧时间计算方法,每帧动画之间如何过渡,类似与UIView的keyframeAnimation。
kCAAnimationLinear 默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算,该模式提供了最大化控制动画的时间
kCAAnimationDiscrete 离散的,不进行插值计算,所有关键帧直接逐个进行显示,该模式使用keyTimess属性,但是忽略timingFunctions属性。
kCAAnimationPaced 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效
kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑
kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的

  • tensionValues: 控制着曲线的紧密度(正值将越紧,负值将越宽松)
  • continuityValues: 控制片段之间的链接(正值将有锋利的圆角,负值将是倒立的圆角)
  • biasValues:定义了曲线发生的地点(正值将在在控制点前移动曲线,负值将在控制点后移动)
  • rotationMode:设置路径旋转,当设置path有不同角度时,会自动旋转layer角度与path相切
CAKeyframeAnimation

动画代码实现:

- (void)pointMoveAn
{
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.path = [self starPath].CGPath;
    layer.strokeColor = [UIColor blueColor].CGColor;
    layer.fillColor = [UIColor lightGrayColor].CGColor;
    [self.view.layer addSublayer:layer];
    
    [self.view addSubview:self.pointView];
    UIBezierPath *path = [self starPath];
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = path.CGPath;
    animation.duration = 4;
    animation.repeatCount = MAXFLOAT;
    animation.fillMode = kCAFillModeForwards;
    [path moveToPoint:CGPointMake(30, 300)];
    [path moveToPoint:CGPointMake(30, 300)];
    [self.pointView.layer addAnimation:animation forKey:@"starAnimation"];
}

- (UIBezierPath *)starPath
{
    UIBezierPath *path = [UIBezierPath new];
    [path moveToPoint:CGPointMake(47, 338)];
    [path addLineToPoint:CGPointMake(105, 330)];
    [path addLineToPoint:CGPointMake(131, 278)];
    [path addLineToPoint:CGPointMake(157, 330)];
    [path addLineToPoint:CGPointMake(216, 338)];
    [path addLineToPoint:CGPointMake(174, 378)];
    [path addLineToPoint:CGPointMake(183, 436)];
    [path addLineToPoint:CGPointMake(131, 409)];
    [path addLineToPoint:CGPointMake(79, 436)];
    [path addLineToPoint:CGPointMake(89, 378)];
    [path closePath];
    path.lineWidth = 1.0f;
    return path;
}

CAAnimationGroup 动画组

  • animations: CAAnimation集合
    使得多个动画同一时间段内并发执行,执行一些单一动画不易实现的效果.
    如下面一段动画,既要改变路径也要改变图形的圆角大小.
CAAnimationGroup

动画代码实现:

    CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(0, 0)],[NSValue valueWithCGPoint:CGPointMake(100, 100)],[NSValue valueWithCGPoint:CGPointMake(0, 200)]];
    animation.duration = 2;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    animation.keyTimes = @[@0,@0.25,@0.5,@0.75,@1];
    animation.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 200, 100, 100)].CGPath;
    
    CABasicAnimation * animation2 = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
    animation2.toValue = @50;
    animation2.duration = 2;
    animation2.fillMode = kCAFillModeForwards;
    animation2.removedOnCompletion = NO;
    
    CAAnimationGroup * group = [CAAnimationGroup animation];
    group.duration = 2;
    group.fillMode = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    group.animations = @[animation,animation2];
    
    [self.testView.layer addAnimation:group forKey:@"group"];

CASpringAnimation是iOS9之后开放出来的API

  • mass: 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大.默认值:1;
  • stiffness: 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快.值必须大于0. 默认值:100;
  • damping:阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快.默认值:10;
  • initialVelocity:速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反.默认值:0;
  • settlingDuration:估算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
CASpringAnimation

动画实现

- (void)springAnimationTextAction:(CGPoint)point {
    
    if (@available(iOS 9.0, *)) {
        CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"bounds"];
        springAnimation.fromValue = [NSValue valueWithCGRect:CGRectMake(point.x, point.y, 60, 60)];
        springAnimation.toValue = [NSValue valueWithCGRect:self.customView.frame];

        springAnimation.mass = 5;
        springAnimation.stiffness = 100;
        springAnimation.damping = 10;
        //速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
        springAnimation.initialVelocity = 10;
        //估算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
        NSLog(@"====%f",springAnimation.settlingDuration);
        springAnimation.duration = springAnimation.settlingDuration;
        springAnimation.removedOnCompletion = NO;
        [self.customView.layer addAnimation:springAnimation forKey:@"springAnimation"];

    } else {
        // Fallback on earlier versions
    }
}

CA动画的基础知识点告一段落了,现在来说一下我们现实开发中可能遇到的一些易错或难点:

fillMode:首先是fillMode,不知道你有没有注意到上面的非active时间段解释,该处于的阶段表示“动画开始之前和动画结束之后”, 动画还有可能被一些突发事件终止,如页面跳转,前后台切换等都会是原先的动画离开active状态.接着看下一个属性

removedOnCompletion: 它同样也是有 active duration相关解释,表示如果为true,动画一旦处于非active段,将会从渲染树移除,如果为false,它会一直存在于内存中,直到图层被销毁。
so,为了防止一些动画被打断,如下拉刷新的自定义loading菊花,需要加上这个属性,否则在页面处于加载的状态时,push下个页面,再pop回来,如果实际还处于loading状态,你就会发现loading动画失效了,就出现UI展示Bug了!

delegate: 有没有注意的它的属性是<strong>, 这样的话我们的容器类会强持有这个animation. 当delegate和removedOnCompletion一同出现的时候,需要手动移除这个animation,否则类不会被释放!

keyPath: keyPath有哪些值,你了解不?为什么那些值可以作为动画的keyPath? 我们知道动画操作在Layer上, CALayer.h 对属性有声明,每一个可以作为keyPath的属性都会有声明Animatable,可动画性!

总结: CAAnimation是一个抽象超类,OC语言也是有抽象类的!
CAAnimation是一个很好的API接口示范例子,继承能实现很多功能,协议可以有不一样的使用方法.

CAAnimation 和CALayer互不相离, CAAnimation与UIView的animation又有怎么样的关系? 下回分析.

相关文章

网友评论

      本文标题:重拾CAAnimation

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