開篇語:
上一篇我們介紹瞭如何使用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。當然,這完全根據實際情況而定。