diff --git a/AsyncDisplayKit/ASMultiplexImageNode.h b/AsyncDisplayKit/ASMultiplexImageNode.h index 73a74cbdeb..0826cd951b 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.h +++ b/AsyncDisplayKit/ASMultiplexImageNode.h @@ -117,6 +117,12 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) { */ @property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier; +/** + * @abstract If the downloader implements progressive image rendering and this value is YES progressive renders of the + * image will be displayed as the image downloads. Defaults to YES. + */ +@property (nonatomic, assign, readwrite) BOOL shouldRenderProgressImages; + #if TARGET_OS_IOS /** * @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used. diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index cb83bb1156..e5d6e47fbe 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -85,6 +85,10 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent ASDN::RecursiveMutex _downloadIdentifierLock; id _downloadIdentifier; + // Properties + ASDN::RecursiveMutex _propertyLock; + BOOL _shouldRenderProgressImages; + //set on init only BOOL _downloaderSupportsNewProtocol; BOOL _downloaderImplementsSetProgress; @@ -186,6 +190,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent _cacheSupportsNewProtocol = [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)]; _cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)]; + _shouldRenderProgressImages = YES; + self.shouldBypassEnsureDisplay = YES; return self; @@ -339,6 +345,27 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent #endif } + +- (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages +{ + ASDN::MutexLocker l(_propertyLock); + if (shouldRenderProgressImages == _shouldRenderProgressImages) { + return; + } + + _shouldRenderProgressImages = shouldRenderProgressImages; + + + ASDN::MutexUnlocker u(_propertyLock); + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (BOOL)shouldRenderProgressImages +{ + ASDN::MutexLocker l(_propertyLock); + return _shouldRenderProgressImages; +} + #pragma mark - #pragma mark - @@ -458,32 +485,34 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent */ - (void)_updateProgressImageBlockOnDownloaderIfNeeded { - // Read our interface state before locking so that we don't lock super while holding our lock. - ASInterfaceState interfaceState = self.interfaceState; - ASDN::MutexLocker l(_downloadIdentifierLock); - - if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil) { + BOOL shouldRenderProgressImages = self.shouldRenderProgressImages; + + // Read our interface state before locking so that we don't lock super while holding our lock. + ASInterfaceState interfaceState = self.interfaceState; + ASDN::MutexLocker l(_downloadIdentifierLock); + + if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil) { + return; + } + + ASImageDownloaderProgressImage progress = nil; + if (shouldRenderProgressImages && ASInterfaceStateIncludesVisible(interfaceState)) { + __weak __typeof__(self) weakSelf = self; + progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { + __typeof__(self) strongSelf = weakSelf; + if (strongSelf == nil) { return; - } - - ASImageDownloaderProgressImage progress = nil; - if (ASInterfaceStateIncludesVisible(interfaceState)) { - __weak __typeof__(self) weakSelf = self; - progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { - __typeof__(self) strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock); - //Getting a result back for a different download identifier, download must not have been successfully canceled - if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { - return; - } - strongSelf.image = progressImage; - }; - } - [_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier]; + } + + ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock); + //Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + strongSelf.image = progressImage; + }; + } + [_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier]; } - (void)_clearImage diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index ef3feba1e1..abbc800840 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -73,6 +73,12 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign, readwrite) BOOL shouldCacheImage; +/** + * If the downloader implements progressive image rendering and this value is YES progressive renders of the + * image will be displayed as the image downloads. Defaults to YES. + */ +@property (nonatomic, assign, readwrite) BOOL shouldRenderProgressImages; + /** * The image quality of the current image. This is a number between 0 and 1 and can be used to track * progressive progress. Calculated by dividing number of bytes / expected number of total bytes. diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index 27a677fe61..c52e01304d 100755 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -45,6 +45,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; BOOL _delegateSupportsDidStartFetchingData; BOOL _delegateSupportsDidFailWithError; BOOL _delegateSupportsImageNodeDidFinishDecoding; + + BOOL _shouldRenderProgressImages; //set on init only BOOL _downloaderSupportsNewProtocol; @@ -83,6 +85,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)]; _shouldCacheImage = YES; + _shouldRenderProgressImages = YES; self.shouldBypassEnsureDisplay = YES; return self; @@ -218,6 +221,26 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; return _delegate; } +- (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages +{ + ASDN::MutexLocker l(_lock); + if (shouldRenderProgressImages == _shouldRenderProgressImages) { + return; + } + + _shouldRenderProgressImages = shouldRenderProgressImages; + + + ASDN::MutexUnlocker u(_lock); + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (BOOL)shouldRenderProgressImages +{ + ASDN::MutexLocker l(_lock); + return _shouldRenderProgressImages; +} + - (BOOL)placeholderShouldPersist { ASDN::MutexLocker l(_lock); @@ -309,6 +332,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; */ - (void)_updateProgressImageBlockOnDownloaderIfNeeded { + BOOL shouldRenderProgressImages = self.shouldRenderProgressImages; + // Read our interface state before locking so that we don't lock super while holding our lock. ASInterfaceState interfaceState = self.interfaceState; ASDN::MutexLocker l(_lock); @@ -318,7 +343,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; } ASImageDownloaderProgressImage progress = nil; - if (ASInterfaceStateIncludesVisible(interfaceState)) { + if (shouldRenderProgressImages && ASInterfaceStateIncludesVisible(interfaceState)) { __weak __typeof__(self) weakSelf = self; progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { __typeof__(self) strongSelf = weakSelf;