Merge pull request #2588 from dreampiggy/bugfix_fl_placeholder_another_taste

Fix the FLAnimatedImage compatible code issue by introduce a private API
This commit is contained in:
DreamPiggy 2019-01-26 16:19:49 +08:00 committed by GitHub
commit c09bd8b42a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 14 deletions

View File

@ -17,6 +17,19 @@
#import "UIImage+MultiFormat.h"
#import "UIImage+MemoryCacheCost.h"
@interface UIView (PrivateWebCache)
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
internalSetImageBlock:(nullable SDInternalSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock
context:(nullable NSDictionary<NSString *, id> *)context;
@end
static inline FLAnimatedImage * SDWebImageCreateFLAnimatedImage(FLAnimatedImageView *imageView, NSData *imageData) {
if ([NSData sd_imageFormatForImageData:imageData] != SDImageFormatGIF) {
return nil;
@ -131,7 +144,7 @@ static inline NSUInteger SDWebImageMemoryCostFLAnimatedImage(FLAnimatedImage *an
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:^(UIImage *image, NSData *imageData) {
internalSetImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
__strong typeof(weakSelf)strongSelf = weakSelf;
if (!strongSelf) {
dispatch_group_leave(group);
@ -150,7 +163,7 @@ static inline NSUInteger SDWebImageMemoryCostFLAnimatedImage(FLAnimatedImage *an
// Step 2. Check if original compressed image data is "GIF"
BOOL isGIF = (image.sd_imageFormat == SDImageFormatGIF || [NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF);
// Check if placeholder, which does not trigger a backup disk cache query
BOOL isPlaceholder = (image == placeholder);
BOOL isPlaceholder = !imageData && image && cacheType == SDImageCacheTypeNone;
if (!isGIF || isPlaceholder) {
strongSelf.image = image;
strongSelf.animatedImage = nil;

View File

@ -24,6 +24,7 @@ FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageExternalCustomManagerKey;
FOUNDATION_EXPORT const int64_t SDWebImageProgressUnitCountUnknown; /* 1LL */
typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
typedef void(^SDInternalSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
@interface UIView (WebCache)

View File

@ -60,6 +60,25 @@ static char TAG_ACTIVITY_SHOW;
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock
context:(nullable NSDictionary<NSString *, id> *)context {
SDInternalSetImageBlock internalSetImageBlock;
if (setImageBlock) {
internalSetImageBlock = ^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (setImageBlock) {
setImageBlock(image, imageData);
}
};
}
[self sd_internalSetImageWithURL:url placeholderImage:placeholder options:options operationKey:operationKey internalSetImageBlock:internalSetImageBlock progress:progressBlock completed:completedBlock context:context];
}
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
internalSetImageBlock:(nullable SDInternalSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock
context:(nullable NSDictionary<NSString *, id> *)context {
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
@ -70,7 +89,7 @@ static char TAG_ACTIVITY_SHOW;
dispatch_group_enter(group);
}
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
});
}
@ -157,7 +176,7 @@ static char TAG_ACTIVITY_SHOW;
#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];
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL];
#endif
if (group) {
// compatible code for FLAnimatedImage, because we assume completedBlock called after image was set. This will be removed in 5.x
@ -190,13 +209,13 @@ static char TAG_ACTIVITY_SHOW;
[self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
}
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDInternalSetImageBlock)setImageBlock cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL {
#if SD_UIKIT || SD_MAC
[self sd_setImage:image imageData:imageData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:0 imageURL:nil];
[self sd_setImage:image imageData:imageData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:cacheType imageURL:imageURL];
#else
// watchOS does not support view transition. Simplify the logic
if (setImageBlock) {
setImageBlock(image, imageData);
setImageBlock(image, imageData, cacheType, imageURL);
} else if ([self isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)self;
[imageView setImage:image];
@ -205,21 +224,21 @@ static char TAG_ACTIVITY_SHOW;
}
#if SD_UIKIT || SD_MAC
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock transition:(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL {
- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDInternalSetImageBlock)setImageBlock transition:(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL {
UIView *view = self;
SDSetImageBlock finalSetImageBlock;
SDInternalSetImageBlock finalSetImageBlock;
if (setImageBlock) {
finalSetImageBlock = setImageBlock;
} else if ([view isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)view;
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData) {
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) {
imageView.image = setImage;
};
}
#if SD_UIKIT
else if ([view isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)view;
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData){
finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) {
[button setImage:setImage forState:UIControlStateNormal];
};
}
@ -235,7 +254,7 @@ static char TAG_ACTIVITY_SHOW;
} completion:^(BOOL finished) {
[UIView transitionWithView:view duration:transition.duration options:transition.animationOptions animations:^{
if (finalSetImageBlock && !transition.avoidAutoSetImage) {
finalSetImageBlock(image, imageData);
finalSetImageBlock(image, imageData, cacheType, imageURL);
}
if (transition.animations) {
transition.animations(view, image);
@ -255,7 +274,7 @@ static char TAG_ACTIVITY_SHOW;
context.timingFunction = transition.timingFunction;
context.allowsImplicitAnimation = (transition.animationOptions & SDWebImageAnimationOptionAllowsImplicitAnimation);
if (finalSetImageBlock && !transition.avoidAutoSetImage) {
finalSetImageBlock(image, imageData);
finalSetImageBlock(image, imageData, cacheType, imageURL);
}
if (transition.animations) {
transition.animations(view, image);
@ -269,7 +288,7 @@ static char TAG_ACTIVITY_SHOW;
#endif
} else {
if (finalSetImageBlock) {
finalSetImageBlock(image, imageData);
finalSetImageBlock(image, imageData, cacheType, imageURL);
}
}
}

View File

@ -140,6 +140,35 @@ static void * SDCategoriesTestsContext = &SDCategoriesTestsContext;
[self waitForExpectationsWithCommonTimeout];
}
- (void)testFLAnimatedImageViewSetImageWithPlaceholderFromCacheForSameURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"FLAnimatedImageView set image with a placeholder which is the same as the cached image for same url"];
/**
This is a really rare case. Some of user, who query the cache key for one GIF url and get the placeholder
Then use the placeholder and trigger a query for same url, because it will hit memory cache immediately, so the two `setImageBlock` call will have the same image instance and hard to distinguish. (Because we should not do async disk cache check for placeholder)
*/
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
NSURL *originalImageURL = [NSURL URLWithString:@"http://assets.sbnation.com/assets/2512203/dogflops.gif"];
NSString *key = [SDWebImageManager.sharedManager cacheKeyForURL:originalImageURL];
[SDWebImageManager.sharedManager loadImageWithURL:originalImageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
UIImage *cachedImage = [SDImageCache.sharedImageCache imageFromCacheForKey:key];
expect(cachedImage).toNot.beNil(); // Should be stored
cachedImage.sd_FLAnimatedImage = nil; // Cleanup the associated FLAnimatedImage instance
[imageView sd_setImageWithURL:originalImageURL
placeholderImage:cachedImage
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
expect(image).to.equal(cachedImage); // should hit the cache and it's the same as placeholder
expect(imageView.animatedImage).toNot.beNil();
[expectation fulfill];
}];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)testUIViewImageProgressKVOWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"UIView imageProgressKVO failed"];
UIView *view = [[UIView alloc] init];