使用SDWebImage加载沙盒里的GIF图片

1,461 阅读5分钟

最近项目中有个需求,需要从后台获取一张GIF,保存到本地.每次请求该接口时,如果有新的GIF,那么需要把新的GIF保存到本地,删除原来的GIF,并且把新的GIF显示到手机上面.

UIImageView按道理说是不能直接显示GIF图片的,但是我们知道GIF图其实是很多张图片,设置好每张图片之间的间隔后,拼成了一张图片.所以我们可以通过拆解下载好的GIF为一张张的图片,把这些图片放到一个数组里面,通过UIImage的类方法+ (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration ;来生成一张GIF图片.把生成好的赋值给UIImageViewimage属性即可.

在SDWebImage 4.0之前的版本(4.0之后,对于GIF图片,提供了一个FLAnimatedImageView来代替UIImageView)我们可以在UIImage+GIF.m文件中可以找到相应的实现.

+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
    if (!data) {
        return nil;
    }

    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    //gif都是一张张图片拼出来的,这里的count就是拼出这个gif图一共用了多少张图片.
    size_t count = CGImageSourceGetCount(source);

    UIImage *animatedImage;
    //如果只是一张图片,那么就简单的进行解码
    if (count <= 1) {
        animatedImage = [[UIImage alloc] initWithData:data];
    }
    else {
        //存放图片的数组.
        NSMutableArray *images = [NSMutableArray array];
        //设置图片的时间长度
        NSTimeInterval duration = 0.0f;

        for (size_t i = 0; i < count; i++) {
            //根据source来获取到每帧图片,并且生成CGImageRef.
            CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
            if (!image) {
                continue;
            }
            //获取gif图片每帧之间的时间间隔并且叠加.
            duration += [self sd_frameDurationAtIndex:i source:source];
            //将生成的每帧图片放到图片数组中.
            [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
            //因为使用了create,所以需要使用release释放掉生成的CGImageRef
            CGImageRelease(image);
        }
        
        if (!duration) {
            duration = (1.0f / 10.0f) * count;
        }
        //生成一张GIF图片.
        animatedImage = [UIImage animatedImageWithImages:images duration:duration];
    }

    CFRelease(source);

    return animatedImage;
}

//获取每帧图片的duration
+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
    float frameDuration = 0.1f;
    CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
    NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
    NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];

    NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
    if (delayTimeUnclampedProp) {
        frameDuration = [delayTimeUnclampedProp floatValue];
    }
    else {

        NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
        if (delayTimeProp) {
            frameDuration = [delayTimeProp floatValue];
        }
    }

    // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
    // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
    // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
    // for more information.
    //很多烦人的广告指定间隔为0来制作一种非常快的闪图.
    //我们遵循Firefox的行为,对于间隔小于10ms的帧
    if (frameDuration < 0.011f) {
        frameDuration = 0.100f;
    }

    CFRelease(cfFrameProperties);
    return frameDuration;
}

主要就是这两段代码.


我的做法是:直接用SDWebImageManager中的下载方法,下载图片pic,再将pic的url保存起来,等到下次请求回来图片的URL时,进行比较,如果一样,那么就直接拿url.absoluteString作为key去磁盘里面去就可以了. 还是比较简单的,如果你想存到自己的文件里面,那也可以,只需要把路径设置好就行了,然后去对应的路径里面,把文件以data的形式加载出来,然后通过上面贴的代码,把data作为参数来获取到GIF图片即可.