这个动画比较简单基础,首先我们来看一下这个动画模拟的动图
下载动画


动画一共有四个状态,开始下载下载结束下载成功下载失败

为什么会分有下载结束这样的状态呢?因为考虑到实际场景,可能会有在加载过程中,用户返回上级界面,我们需要结束下载动画


我们按状态来实现,首先先看开始加载

1
2
3
4
5
6
7
8
9
10
11
12
CABasicAnimation *rotationZAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationZAnimation.fromValue = @(0);
rotationZAnimation.toValue = @(M_PI*2);
rotationZAnimation.repeatDuration = HUGE_VAL;
rotationZAnimation.duration = 1.0;
rotationZAnimation.cumulative = YES;
rotationZAnimation.beginTime = CACurrentMediaTime();
[self.loadingLayer addAnimation:rotationZAnimation forKey:@"rotationZAnimation"];
        
NSArray *values = [self valueArrayWithWidth:self.downloadingViewWidth];
CAKeyframeAnimation *boundsAnimation = [self bounsAnimationWithValues:values];
[self.loadingLayer addAnimation:boundsAnimation forKey:nil];

这里分为两个动画,一个是旋转动画,一个是放大缩小的动画
旋转动画,我们只有一个初始值和末值,所以我们只需要使用CABasicAnimation就可以了,不需要使用CAKeyframeAnimation,CAKeyframeAnimation是用来处理关键帧动画的,它的values属性用来存储关键帧的值,这就是我们用来做处理放大缩小动画,上面代码可以看到我将它抽成了一个方法来用,因为在下载成功和失败的时候,成功和失败也是有放大缩小的动画,这三个其实是一个动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (NSArray *)valueArrayWithWidth:(CGFloat)width {
    return @[[NSValue valueWithCGRect:CGRectMake(0, 0, width * 0.7, width * 0.7)],
             [NSValue valueWithCGRect:CGRectMake(0, 0, width, width)],
             [NSValue valueWithCGRect:CGRectMake(0, 0, width * 0.9, width * 0.9)]];
}

- (CAKeyframeAnimation *)bounsAnimationWithValues:(NSArray *)values {
    CAKeyframeAnimation *boundsAnimation = [CAKeyframeAnimation animationWithKeyPath:@"bounds"];
    boundsAnimation.duration = 0.6;
    boundsAnimation.beginTime = CACurrentMediaTime();
    boundsAnimation.values = values;
    boundsAnimation.keyTimes = @[@(0),@(0.3),@(0.6)];
    boundsAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                        [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                        [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    boundsAnimation.removedOnCompletion = NO;
    boundsAnimation.fillMode = kCAFillModeForwards;
    return boundsAnimation;
}

我们统一设置动画的values值,这里传入的width值是下载的背景大小,因为下载圆圈有个放大的过程,所以我们需要圆圈的原始大小要比背景的大小小,所以我默认设置圆圈的大小是背景大小的0.9倍,所以动画的效果是圆圈从背景大小的0.7倍->背景大小->背景大小的0.9倍(圆圈的原始大小)
在设置这个动画的时候,因为动画的值不止是初始值和末值,还是中间值,所以我使用CAKeyframeAnimation来做,每个关键帧之间的动画时间设置为0.3秒,这是最适合的动画时间,当然这是时间还是由自己反复调试动画决定的


下载结束我们只要将转圈的动画移除掉即可,这样转圈就恢复到原来的状态了

1
[self.loadingLayer removeAllAnimations];


下载成功我们也需要将转圈动画移除,然后出现成功的动画,并且将成功图层的透明度从0变到1

1
2
3
4
5
6
7
8
9
[self.loadingLayer removeAllAnimations];
self.failLayer.opacity = 0.0;
NSArray *values = [self valueArrayWithWidth:self.downloadingViewWidth * 0.5];
CAKeyframeAnimation *boundsAnimation = [self bounsAnimationWithValues:values];
[self.doneLayer addAnimation:boundsAnimation forKey:nil];
        
[UIView animateWithDuration:1.0 animations:^{
    self.doneLayer.opacity = 1.0;
}];


下载失败也是同理

1
2
3
4
5
6
7
8
9
[self.loadingLayer removeAllAnimations];
self.doneLayer.opacity = 0.0;
NSArray *values = [self valueArrayWithWidth:self.downloadingViewWidth * 0.5];
CAKeyframeAnimation *boundsAnimation = [self bounsAnimationWithValues:values];
[self.failLayer addAnimation:boundsAnimation forKey:nil];
        
[UIView animateWithDuration:1.0 animations:^{
     self.failLayer.opacity = 1.0;
}];

好了,动画主要的原理就是这样,具体的代码实现可以看这里:https://github.com/Yuzeyang/DownloadingAnimation