Merge pull request #2393 from dreampiggy/bugfix_FLAnimatedImage_display_issue

Fix that FLAnimatedImageView+WebCache display behavior for GIF images and other images format
This commit is contained in:
Bogdan Poplauschi 2018-07-18 16:26:38 +03:00 committed by GitHub
commit 98a2d9a3b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 67 deletions

View File

@ -14,6 +14,21 @@
#import "UIView+WebCache.h" #import "UIView+WebCache.h"
#import "NSData+ImageContentType.h" #import "NSData+ImageContentType.h"
#import "UIImageView+WebCache.h" #import "UIImageView+WebCache.h"
#import "UIImage+MultiFormat.h"
static inline FLAnimatedImage * SDWebImageCreateFLAnimatedImage(FLAnimatedImageView *imageView, NSData *imageData) {
if ([NSData sd_imageFormatForImageData:imageData] != SDImageFormatGIF) {
return nil;
}
FLAnimatedImage *animatedImage;
// Compatibility in 4.x for lower version FLAnimatedImage.
if ([FLAnimatedImage respondsToSelector:@selector(initWithAnimatedGIFData:optimalFrameCacheSize:predrawingEnabled:)]) {
animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:imageView.sd_optimalFrameCacheSize predrawingEnabled:imageView.sd_predrawingEnabled];
} else {
animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData];
}
return animatedImage;
}
@implementation UIImage (FLAnimatedImage) @implementation UIImage (FLAnimatedImage)
@ -98,61 +113,52 @@
options:(SDWebImageOptions)options options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock { completed:(nullable SDExternalCompletionBlock)completedBlock {
dispatch_group_t group = dispatch_group_create();
__weak typeof(self)weakSelf = self; __weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url [self sd_internalSetImageWithURL:url
placeholderImage:placeholder placeholderImage:placeholder
options:options options:options
operationKey:nil operationKey:nil
setImageBlock:^(UIImage *image, NSData *imageData) { setImageBlock:^(UIImage *image, NSData *imageData) {
// We could not directlly create the animated image on bacakground queue because it's time consuming, by the time we set it back, the current runloop has passed and the placeholder has been rendered and then replaced with animated image, this cause a flashing. __strong typeof(weakSelf)strongSelf = weakSelf;
// Previously we use a trick to firstly set the static poster image, then set animated image back to avoid flashing, but this trick fail when using with custom UIView transition. Core Animation will use the current layer state to do rendering, so even we later set it back, the transition will not update. (it's recommended to use `SDWebImageTransition` instead) if (!strongSelf) {
// So we have no choice to force store the FLAnimatedImage into memory cache using a associated object binding to UIImage instance. This consumed memory is adoptable and much smaller than `_UIAnimatedImage` for big GIF return;
}
// Step 1. Check memory cache (associate object)
FLAnimatedImage *associatedAnimatedImage = image.sd_FLAnimatedImage; FLAnimatedImage *associatedAnimatedImage = image.sd_FLAnimatedImage;
if (associatedAnimatedImage) { if (associatedAnimatedImage) {
// Asscociated animated image exist // Asscociated animated image exist
weakSelf.animatedImage = associatedAnimatedImage; strongSelf.animatedImage = associatedAnimatedImage;
weakSelf.image = nil; strongSelf.image = nil;
if (group) { return;
dispatch_group_leave(group); }
// Step 2. Check if original compressed image data is "GIF"
BOOL isGIF = (image.sd_imageFormat == SDImageFormatGIF || [NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF);
if (!isGIF) {
strongSelf.image = image;
strongSelf.animatedImage = nil;
return;
}
// Step 3. Check if data exist or query disk cache
if (!imageData) {
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
imageData = [[SDImageCache sharedImageCache] diskImageDataForKey:key];
}
// Step 4. Create FLAnimatedImage
FLAnimatedImage *animatedImage = SDWebImageCreateFLAnimatedImage(strongSelf, imageData);
// Step 5. Set animatedImage or normal image
if (animatedImage) {
if (strongSelf.sd_cacheFLAnimatedImage) {
image.sd_FLAnimatedImage = animatedImage;
} }
} else if ([NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF) { strongSelf.animatedImage = animatedImage;
// Firstly set the static poster image to avoid flashing strongSelf.image = nil;
UIImage *posterImage = image.images ? image.images.firstObject : image;
weakSelf.image = posterImage;
weakSelf.animatedImage = nil;
// Secondly create FLAnimatedImage in global queue because it's time consuming, then set it back
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
FLAnimatedImage *animatedImage;
// Compatibility in 4.x for lower version FLAnimatedImage.
if ([FLAnimatedImage respondsToSelector:@selector(initWithAnimatedGIFData:optimalFrameCacheSize:predrawingEnabled:)]) {
animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:weakSelf.sd_optimalFrameCacheSize predrawingEnabled:weakSelf.sd_predrawingEnabled];
} else {
animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.sd_cacheFLAnimatedImage) {
image.sd_FLAnimatedImage = animatedImage;
}
weakSelf.animatedImage = animatedImage;
weakSelf.image = nil;
if (group) {
dispatch_group_leave(group);
}
});
});
} else { } else {
// Not animated image strongSelf.animatedImage = nil;
weakSelf.image = image; strongSelf.image = image;
weakSelf.animatedImage = nil;
if (group) {
dispatch_group_leave(group);
}
} }
} }
progress:progressBlock progress:progressBlock
completed:completedBlock completed:completedBlock];
context:group ? @{SDWebImageInternalSetImageGroupKey : group} : nil];
} }
@end @end

View File

@ -13,7 +13,7 @@
/** /**
A Dispatch group to maintain setImageBlock and completionBlock. This key should be used only internally and may be changed in the future. (dispatch_group_t) A Dispatch group to maintain setImageBlock and completionBlock. This key should be used only internally and may be changed in the future. (dispatch_group_t)
*/ */
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageInternalSetImageGroupKey; FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageInternalSetImageGroupKey __deprecated_msg("Key Deprecated. Does nothing. This key should be used only internally");
/** /**
A SDWebImageManager instance to control the image download and cache process using in UIImageView+WebCache category and likes. If not provided, use the shared manager (SDWebImageManager) A SDWebImageManager instance to control the image download and cache process using in UIImageView+WebCache category and likes. If not provided, use the shared manager (SDWebImageManager)
*/ */

View File

@ -65,10 +65,6 @@ static char TAG_ACTIVITY_SHOW;
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!(options & SDWebImageDelayPlaceholder)) { if (!(options & SDWebImageDelayPlaceholder)) {
if ([context valueForKey:SDWebImageInternalSetImageGroupKey]) {
dispatch_group_t group = [context valueForKey:SDWebImageInternalSetImageGroupKey];
dispatch_group_enter(group);
}
dispatch_main_async_safe(^{ dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
}); });
@ -152,30 +148,14 @@ static char TAG_ACTIVITY_SHOW;
transition = sself.sd_imageTransition; transition = sself.sd_imageTransition;
} }
#endif #endif
if ([context valueForKey:SDWebImageInternalSetImageGroupKey]) { dispatch_main_async_safe(^{
dispatch_group_t group = [context valueForKey:SDWebImageInternalSetImageGroupKey];
dispatch_group_enter(group);
dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC #if SD_UIKIT || SD_MAC
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else #else
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endif #endif
}); callCompletedBlockClojure();
// ensure completion block is called after custom setImage process finish });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
callCompletedBlockClojure();
});
} else {
dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endif
callCompletedBlockClojure();
});
}
}]; }];
[self sd_setImageLoadOperation:operation forKey:validOperationKey]; [self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else { } else {