mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-04-24 03:45:58 +08:00
Add the ability for ASNetworkImageNodes to keep track of their progressive image quality
This commit is contained in:
@@ -337,6 +337,10 @@
|
||||
9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
|
||||
A2763D791CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */; };
|
||||
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */; };
|
||||
A2763D7B1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */; };
|
||||
A2763D7C1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */; };
|
||||
A32FEDD51C501B6A004F642A /* ASTextKitFontSizeAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
A373200F1C571B730011FC94 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = A373200E1C571B050011FC94 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = A373200E1C571B050011FC94 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -808,6 +812,8 @@
|
||||
9C8898BA1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASTextKitFontSizeAdjuster.mm; path = TextKit/ASTextKitFontSizeAdjuster.mm; sourceTree = "<group>"; };
|
||||
9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = "<group>"; };
|
||||
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASPINRemoteImageDownloader.h; path = Details/ASPINRemoteImageDownloader.h; sourceTree = "<group>"; };
|
||||
A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASPINRemoteImageDownloader.m; path = Details/ASPINRemoteImageDownloader.m; sourceTree = "<group>"; };
|
||||
A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitFontSizeAdjuster.h; path = TextKit/ASTextKitFontSizeAdjuster.h; sourceTree = "<group>"; };
|
||||
A373200E1C571B050011FC94 /* ASTextNode+Beta.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASTextNode+Beta.h"; sourceTree = "<group>"; };
|
||||
AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = "<group>"; };
|
||||
@@ -1064,6 +1070,8 @@
|
||||
055B9FA71A1C154B00035D6D /* ASNetworkImageNode.mm */,
|
||||
25E327541C16819500A2170C /* ASPagerNode.h */,
|
||||
25E327551C16819500A2170C /* ASPagerNode.m */,
|
||||
A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */,
|
||||
A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */,
|
||||
D785F6601A74327E00291744 /* ASScrollNode.h */,
|
||||
D785F6611A74327E00291744 /* ASScrollNode.m */,
|
||||
B0F880581BEAEC7500D17647 /* ASTableNode.h */,
|
||||
@@ -1461,6 +1469,7 @@
|
||||
058D0A72195D05F800B7D73C /* _ASCoreAnimationExtras.h in Headers */,
|
||||
7A06A73B1C35F08800FE8DAA /* ASRelativeLayoutSpec.h in Headers */,
|
||||
68B8A4DC1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h in Headers */,
|
||||
A2763D791CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */,
|
||||
058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */,
|
||||
058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */,
|
||||
B13CA0F71C519E9400E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */,
|
||||
@@ -1620,6 +1629,7 @@
|
||||
B35062581B010F070018CF92 /* ASAvailability.h in Headers */,
|
||||
DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */,
|
||||
254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */,
|
||||
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */,
|
||||
254C6B7A1BF94DF4003EC431 /* ASTextKitRenderer.h in Headers */,
|
||||
69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */,
|
||||
68355B3F1CB57A64001D4E68 /* ASPINRemoteImageDownloader.h in Headers */,
|
||||
|
||||
@@ -103,4 +103,13 @@ ASDISPLAYNODE_EXTERN_C_END
|
||||
*/
|
||||
- (void)cancelLayoutTransitionsInProgress;
|
||||
|
||||
/**
|
||||
* @abstract Indicates that the receiver and all subnodes have finished displaying. May be called more than once, for example if the receiver has
|
||||
* a network image node. This is called after the first display pass even if network image nodes have not downloaded anything (text would be done,
|
||||
* and other nodes that are ready to do their final display). Each render of every progressive jpeg network node would cause this to be called, so
|
||||
* this hook could be called up to 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls the
|
||||
* progressImage block.
|
||||
*/
|
||||
- (void)hierarchyDisplayDidFinish;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1722,19 +1722,23 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
|
||||
[_pendingDisplayNodes removeObject:node];
|
||||
|
||||
if (_pendingDisplayNodes.count == 0 && _placeholderLayer.superlayer && ![self placeholderShouldPersist]) {
|
||||
void (^cleanupBlock)() = ^{
|
||||
[_placeholderLayer removeFromSuperlayer];
|
||||
};
|
||||
if (_pendingDisplayNodes.count == 0) {
|
||||
[self hierarchyDisplayDidFinish];
|
||||
|
||||
if (_placeholderLayer.superlayer && ![self placeholderShouldPersist]) {
|
||||
void (^cleanupBlock)() = ^{
|
||||
[_placeholderLayer removeFromSuperlayer];
|
||||
};
|
||||
|
||||
if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:cleanupBlock];
|
||||
[CATransaction setAnimationDuration:_placeholderFadeDuration];
|
||||
_placeholderLayer.opacity = 0.0;
|
||||
[CATransaction commit];
|
||||
} else {
|
||||
cleanupBlock();
|
||||
if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:cleanupBlock];
|
||||
[CATransaction setAnimationDuration:_placeholderFadeDuration];
|
||||
_placeholderLayer.opacity = 0.0;
|
||||
[CATransaction commit];
|
||||
} else {
|
||||
cleanupBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2390,6 +2394,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hierarchyDisplayDidFinish
|
||||
{
|
||||
// subclass hook
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
// subclass hook
|
||||
|
||||
@@ -469,7 +469,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
ASImageDownloaderProgressImage progress = nil;
|
||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
|
||||
progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
|
||||
@@ -73,6 +73,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldCacheImage;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) CGFloat currentImageQuality;
|
||||
|
||||
/**
|
||||
* The image quality (value between 0 and 1) of the last image that completed displaying.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) CGFloat renderedImageQuality;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -39,17 +39,19 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
id _downloadIdentifier;
|
||||
|
||||
BOOL _imageLoaded;
|
||||
|
||||
CGFloat _currentImageQuality;
|
||||
CGFloat _renderedImageQuality;
|
||||
|
||||
BOOL _delegateSupportsDidStartFetchingData;
|
||||
BOOL _delegateSupportsDidFailWithError;
|
||||
BOOL _delegateSupportsImageNodeDidFinishDecoding;
|
||||
|
||||
|
||||
//set on init only
|
||||
BOOL _downloaderSupportsNewProtocol;
|
||||
BOOL _downloaderImplementsSetProgress;
|
||||
BOOL _downloaderImplementsSetPriority;
|
||||
BOOL _downloaderImplementsAnimatedImage;
|
||||
|
||||
|
||||
BOOL _cacheSupportsNewProtocol;
|
||||
BOOL _cacheSupportsClearing;
|
||||
BOOL _cacheSupportsSynchronousFetch;
|
||||
@@ -120,9 +122,13 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
|
||||
_URL = URL;
|
||||
|
||||
if (reset || _URL == nil)
|
||||
if (reset || _URL == nil) {
|
||||
self.image = _defaultImage;
|
||||
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_currentImageQuality = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
if (self.interfaceState & ASInterfaceStateFetchData) {
|
||||
[self fetchData];
|
||||
}
|
||||
@@ -145,6 +151,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
_defaultImage = defaultImage;
|
||||
|
||||
if (!_imageLoaded) {
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_currentImageQuality = 0.0;
|
||||
});
|
||||
_lock.unlock();
|
||||
// Locking: it is important to release _lock before entering setImage:, as it needs to release the lock before -invalidateCalculatedLayout.
|
||||
// If we continue to hold the lock here, it will still be locked until the next unlock() call, causing a possible deadlock with
|
||||
@@ -161,6 +170,30 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
return _defaultImage;
|
||||
}
|
||||
|
||||
- (void)setCurrentImageQuality:(CGFloat)currentImageQuality
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
_currentImageQuality = currentImageQuality;
|
||||
}
|
||||
|
||||
- (CGFloat)currentImageQuality
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
return _currentImageQuality;
|
||||
}
|
||||
|
||||
- (void)setRenderedImageQuality:(CGFloat)renderedImageQuality
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
_renderedImageQuality = renderedImageQuality;
|
||||
}
|
||||
|
||||
- (CGFloat)renderedImageQuality
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
return _renderedImageQuality;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<ASNetworkImageNodeDelegate>)delegate
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
@@ -196,6 +229,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
if (result) {
|
||||
self.image = result;
|
||||
_imageLoaded = YES;
|
||||
_currentImageQuality = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,7 +310,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
ASImageDownloaderProgressImage progress = nil;
|
||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
|
||||
progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
@@ -288,6 +322,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
return;
|
||||
}
|
||||
strongSelf.image = progressImage;
|
||||
ASPerformBlockOnMainThread(^{
|
||||
strongSelf->_currentImageQuality = progress;
|
||||
});
|
||||
};
|
||||
}
|
||||
[_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
|
||||
@@ -310,6 +347,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
self.animatedImage = nil;
|
||||
self.image = _defaultImage;
|
||||
_imageLoaded = NO;
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_currentImageQuality = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_cancelImageDownload
|
||||
@@ -393,6 +433,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
}
|
||||
|
||||
_imageLoaded = YES;
|
||||
self.currentImageQuality = 1.0;
|
||||
[_delegate imageNode:self didLoadImage:self.image];
|
||||
});
|
||||
}
|
||||
@@ -418,6 +459,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
} else {
|
||||
strongSelf.image = [imageContainer asdk_image];
|
||||
}
|
||||
strongSelf->_currentImageQuality = 1.0;
|
||||
}
|
||||
|
||||
strongSelf->_downloadIdentifier = nil;
|
||||
@@ -472,13 +514,14 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
|
||||
#pragma mark - ASDisplayNode+Subclasses
|
||||
|
||||
- (void)asyncdisplaykit_asyncTransactionContainerStateDidChange
|
||||
- (void)displayDidFinish
|
||||
{
|
||||
if (self.asyncdisplaykit_asyncTransactionContainerState == ASAsyncTransactionContainerStateNoTransactions) {
|
||||
ASDN::MutexLocker l(_lock);
|
||||
if (self.layer.contents != nil && _delegateSupportsImageNodeDidFinishDecoding) {
|
||||
[self.delegate imageNodeDidFinishDecoding:self];
|
||||
}
|
||||
[super displayDidFinish];
|
||||
|
||||
ASDN::MutexLocker l(_lock);
|
||||
if (_delegateSupportsImageNodeDidFinishDecoding && self.layer.contents != nil) {
|
||||
_renderedImageQuality = _currentImageQuality;
|
||||
[self.delegate imageNodeDidFinishDecoding:self];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
|
||||
|
||||
typedef void(^ASImageDownloaderCompletion)(id <ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier);
|
||||
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
|
||||
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, id _Nullable downloadIdentifier);
|
||||
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, CGFloat progress, id _Nullable downloadIdentifier);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
|
||||
ASImageDownloaderPriorityPreload = 0,
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
if (progressBlock) {
|
||||
[[self sharedPINRemoteImageManager] setProgressImageCallback:^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||
dispatch_async(callbackQueue, ^{
|
||||
progressBlock(result.image, result.UUID);
|
||||
progressBlock(result.image, result.renderedImageQuality, result.UUID);
|
||||
});
|
||||
} ofTaskWithUUID:downloadIdentifier];
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user