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:
commit
c09bd8b42a
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue