开篇语:
上一篇我们介绍了如何使用FLAnimatedImage类库加载gif动画,但是,当我们采用了此方案之后发现,牵连的地方还真不少。首先,所有UIImageView都需要更换一边。其次以往传递UIImage的地方需要换成FLAnimatedImage,这修改量可就大了。如何才能继续优雅的使用UIImage呢?
UIImage:
实际上,UIImage是支持创建动图的,但是方法有些个性:
+ (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration NS_AVAILABLE_IOS(5_0);
可以看到初始化时需要传递一个UIImage的数组。那么这个数组从何而来呢?这就涉及到如何提取gif每一帧的图片啦。当然这类代码很多,例如3.X版本的SDWebImage就有相关的代码。
参考代码:
/**
初始化UIImage动图
@param data gif资源
@param scale 缩放比例
@return UIImage动图
*/
+ (UIImage *)animatedImageWithData:(NSData *)data scale:(CGFloat)scale {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage = nil;
if (count <= 1) {
animatedImage = [[UIImage alloc] initWithData:data];
} else {
NSMutableArray<UIImage *> *images = [[NSMutableArray alloc] init];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
duration += [self frameDurationAtIndex:i source:source];
UIImage *frameImage = [UIImage imageWithCGImage:image scale:scale orientation:UIImageOrientationUp];
[images addObject:frameImage];
CGImageRelease(image);
}
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
CFRelease(source);
return animatedImage;
}
/**
The duration of the animation.
*/
+ (float)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
NSDictionary<NSString *, NSDictionary *> *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary<NSString *, NSNumber *> *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];
}
}
CFRelease(cfFrameProperties);
return frameDuration;
}
此时有了可以动的UIImage,那相应的UIImageView就不用再动代码啦。
下面我们来看看如果优雅的结合SDWebImage(主要是考虑使用它的异步网络加载、内存管理等优秀特点)。
//这里省略初始化UIImageView的代码
NSURL *imageUrl = [NSURL URLWithString:@"http://demo.gif"];
//在SDWebImage的setImageBlock块中处理GIF逻辑
[imageView sd_internalSetImageWithURL:imageUrl placeholderImage:nil options:0 operationKey:nil setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
//避免闪屏,先赋值一下
imageView.image = image;
//这里判断GIF资源图片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
__block NSData *gifData = imageData;
if (!gifData) {
//得到缓存
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:imageUrl];
gifData = [[SDImageCache sharedImageCache] diskImageDataForKey:key];
}
UIImage *animatedImage = [UIImage animatedImageWithData:gifData];
//如果是GIF图的话,再赋值
if (animatedImage.images) {
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = animatedImage;
});
}
});
} progress:nil completed:nil];
这里注意,gifData可能是空值,需要我们去缓存管理器中主动获取一下。
总结:
FLAnimatedImage确实是加载gif图片的好方法,但有时候考虑到后期维护、扩展性等方面,我们不得不屈服现实代码。最后项目中我们选择了UIImage。当然,这完全根据实际情况而定。