From 8408b79cb30363bcb431590e3a72aa08d38555cc Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 24 Feb 2024 16:42:47 +0800 Subject: [PATCH] Added `SDWebImageWaitTransition` to wait for transition finished and then callback `completedBlock` This can solve the simple case when people want to do another transition inside completedBlock and mass-up the UI status --- SDWebImage/Core/SDWebImageDefine.h | 9 +++++++ SDWebImage/Core/UIView+WebCache.m | 39 +++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/SDWebImage/Core/SDWebImageDefine.h b/SDWebImage/Core/SDWebImageDefine.h index f6ae2d2d..b5a23f31 100644 --- a/SDWebImage/Core/SDWebImageDefine.h +++ b/SDWebImage/Core/SDWebImageDefine.h @@ -218,6 +218,15 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { * @note This options is UI level options, has no usage on ImageManager or other components. */ SDWebImageAvoidAutoCancelImage = 1 << 24, + + /** + * By defaults, for `SDWebImageTransition`, we just submit to UI transition and inmeediatelly callback the final `completedBlock` (`SDExternalCompletionBlock/SDInternalCompletionBlock`). + * This may cause un-wanted behavior if you do another transition inside `completedBlock`, because the previous transition is still runnning and un-cancellable, which mass-up the UI status. + * For this case, you can pass this option, we will delay the final callback, until your transition end. So when you inside `completedBlock`, no any transition is running on image view and safe to submit new transition. + * @note Currently we do not support `pausable/cancellable` transition. But possible in the future by using the https://developer.apple.com/documentation/uikit/uiviewpropertyanimator. + * @note If you have complicated transition animation, just use `SDWebImageManager` and do UI state management by yourself, do not use the top-level API (`sd_setImageWithURL:`) + */ + SDWebImageWaitTransition = 1 << 25, }; diff --git a/SDWebImage/Core/UIView+WebCache.m b/SDWebImage/Core/UIView+WebCache.m index 23d0d739..f456b718 100644 --- a/SDWebImage/Core/UIView+WebCache.m +++ b/SDWebImage/Core/UIView+WebCache.m @@ -232,11 +232,11 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; #endif dispatch_main_async_safe(^{ #if SD_UIKIT || SD_MAC - [self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; + [self sd_setImage:targetImage imageData:targetData options:options basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL callback:callCompletedBlockClosure]; #else [self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL]; -#endif callCompletedBlockClosure(); +#endif }); }]; [self sd_setImageLoadOperation:operation forKey:validOperationKey]; @@ -263,9 +263,10 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; [self sd_cancelImageLoadOperationWithKey:self.sd_latestOperationKey]; } +// Set image logic without transition (like placeholder and watchOS) - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL { #if SD_UIKIT || SD_MAC - [self sd_setImage:image imageData:imageData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:cacheType imageURL:imageURL]; + [self sd_setImage:image imageData:imageData options:0 basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:cacheType imageURL:imageURL callback:nil]; #else // watchOS does not support view transition. Simplify the logic if (setImageBlock) { @@ -277,8 +278,9 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; #endif } +// Set image logic with transition #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 options:(SDWebImageOptions)options basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock transition:(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL callback:(SDWebImageNoParamsBlock)callback { UIView *view = self; SDSetImageBlock finalSetImageBlock; if (setImageBlock) { @@ -306,6 +308,7 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; } #endif + BOOL waitTransition = SD_OPTIONS_CONTAINS(options, SDWebImageWaitTransition); if (transition) { NSString *originalOperationKey = view.sd_latestOperationKey; @@ -336,6 +339,11 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; if (transition.completion) { transition.completion(finished); } + if (waitTransition) { + if (callback) { + callback(); + } + } }]; }]; #elif SD_MAC @@ -380,12 +388,35 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; if (transition.completion) { transition.completion(YES); } + if (waitTransition) { + if (callback) { + callback(); + } + } }]; }]; #endif + if (!waitTransition) { + if (callback) { + callback(); + } + } } else { if (finalSetImageBlock) { finalSetImageBlock(image, imageData, cacheType, imageURL); + // TODO, in 6.0 + // for `waitTransition`, the `setImageBlock` will provide a extra `completionHandler` params + // Execute `callback` only after that completionHandler is called + if (waitTransition) { + if (callback) { + callback(); + } + } + } + if (!waitTransition) { + if (callback) { + callback(); + } } } }