mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 20:00:38 +00:00
Merge remote-tracking branch 'facebook/master' into ASVideoPlayerNode
This commit is contained in:
commit
b09d0dca9e
@ -125,6 +125,10 @@
|
|||||||
{
|
{
|
||||||
CGSize oldSize = self.calculatedSize;
|
CGSize oldSize = self.calculatedSize;
|
||||||
[super __setNeedsLayout];
|
[super __setNeedsLayout];
|
||||||
|
|
||||||
|
//Adding this lock because lock used to be held when this method was called. Not sure if it's necessary for
|
||||||
|
//didRelayoutFromOldSize:toNewSize:
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
[self didRelayoutFromOldSize:oldSize toNewSize:self.calculatedSize];
|
[self didRelayoutFromOldSize:oldSize toNewSize:self.calculatedSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -373,7 +373,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
_pendingViewState = nil;
|
_pendingViewState = nil;
|
||||||
|
|
||||||
_displaySentinel = nil;
|
_displaySentinel = nil;
|
||||||
_transitionSentinel = nil;
|
|
||||||
|
|
||||||
_pendingDisplayNodes = nil;
|
_pendingDisplayNodes = nil;
|
||||||
}
|
}
|
||||||
@ -654,6 +653,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
return (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize));
|
return (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Layout Transition
|
||||||
|
|
||||||
- (void)transitionLayoutWithAnimation:(BOOL)animated
|
- (void)transitionLayoutWithAnimation:(BOOL)animated
|
||||||
shouldMeasureAsync:(BOOL)shouldMeasureAsync
|
shouldMeasureAsync:(BOOL)shouldMeasureAsync
|
||||||
measurementCompletion:(void(^)())completion
|
measurementCompletion:(void(^)())completion
|
||||||
@ -681,10 +682,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one.");
|
ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t transitionID = [self _newTransitionID];
|
int32_t transitionID = [self _startNewTransition];
|
||||||
|
|
||||||
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
||||||
ASDisplayNodeAssert([node _hasTransitionsInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
|
ASDisplayNodeAssert([node _hasTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
|
||||||
node.hierarchyState |= ASHierarchyStateLayoutPending;
|
node.hierarchyState |= ASHierarchyStateLayoutPending;
|
||||||
node.pendingTransitionID = transitionID;
|
node.pendingTransitionID = transitionID;
|
||||||
});
|
});
|
||||||
@ -726,13 +727,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
ASSizeRange previousConstrainedSize = _constrainedSize;
|
ASSizeRange previousConstrainedSize = _constrainedSize;
|
||||||
[self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:nil];
|
[self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:nil];
|
||||||
|
|
||||||
[self _invalidateTransitionSentinel];
|
|
||||||
|
|
||||||
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
||||||
[node applyPendingLayoutContext];
|
[node applyPendingLayoutContext];
|
||||||
[node _completeLayoutCalculation];
|
[node _completeLayoutCalculation];
|
||||||
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
|
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[self _finishOrCancelTransition];
|
||||||
|
|
||||||
if (completion) {
|
if (completion) {
|
||||||
completion();
|
completion();
|
||||||
@ -781,7 +782,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)calculatedLayoutDidChange
|
- (void)calculatedLayoutDidChange
|
||||||
{
|
{
|
||||||
// subclass override
|
// subclass override
|
||||||
@ -790,9 +790,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
- (void)cancelLayoutTransitionsInProgress
|
- (void)cancelLayoutTransitionsInProgress
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
if ([self _hasTransitionsInProgress]) {
|
if ([self _hasTransitionInProgress]) {
|
||||||
// Invalidate transition sentinel to cancel transitions in progress
|
// Cancel transition in progress
|
||||||
[self _invalidateTransitionSentinel];
|
[self _finishOrCancelTransition];
|
||||||
|
|
||||||
// Tell subnodes to exit layout pending state and clear related properties
|
// Tell subnodes to exit layout pending state and clear related properties
|
||||||
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
||||||
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
|
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
|
||||||
@ -800,8 +801,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Layout Transition
|
|
||||||
|
|
||||||
- (BOOL)usesImplicitHierarchyManagement
|
- (BOOL)usesImplicitHierarchyManagement
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
@ -814,6 +813,33 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
_usesImplicitHierarchyManagement = value;
|
_usesImplicitHierarchyManagement = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)_hasTransitionInProgress
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
return _transitionInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new transition and returns the transition id
|
||||||
|
- (int32_t)_startNewTransition
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
_transitionInProgress = YES;
|
||||||
|
_transitionID = OSAtomicAdd32(1, &_transitionID);
|
||||||
|
return _transitionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_finishOrCancelTransition
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
_transitionInProgress = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_shouldAbortTransitionWithID:(int32_t)transitionID
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
return (!_transitionInProgress || _transitionID != transitionID);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
|
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
|
||||||
{
|
{
|
||||||
[self __layoutSublayouts];
|
[self __layoutSublayouts];
|
||||||
@ -827,6 +853,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
_pendingLayoutContext = nil;
|
_pendingLayoutContext = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - _ASTransitionContextCompletionDelegate
|
#pragma mark - _ASTransitionContextCompletionDelegate
|
||||||
|
|
||||||
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
|
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
|
||||||
@ -1001,27 +1028,31 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
[self invalidateCalculatedLayout];
|
[self invalidateCalculatedLayout];
|
||||||
|
|
||||||
if (_supernode) {
|
if (_supernode) {
|
||||||
|
ASDisplayNode *supernode = _supernode;
|
||||||
|
ASDN::MutexUnlocker u(_propertyLock);
|
||||||
// Cause supernode's layout to be invalidated
|
// Cause supernode's layout to be invalidated
|
||||||
[_supernode setNeedsLayout];
|
// We need to release the lock to prevent a deadlock
|
||||||
} else {
|
[supernode setNeedsLayout];
|
||||||
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
|
return;
|
||||||
[self measureWithSizeRange:oldConstrainedSize];
|
}
|
||||||
|
|
||||||
|
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
|
||||||
|
[self measureWithSizeRange:oldConstrainedSize];
|
||||||
|
|
||||||
CGRect oldBounds = self.bounds;
|
CGRect oldBounds = self.bounds;
|
||||||
CGSize oldSize = oldBounds.size;
|
CGSize oldSize = oldBounds.size;
|
||||||
CGSize newSize = _layout.size;
|
CGSize newSize = _layout.size;
|
||||||
|
|
||||||
|
if (! CGSizeEqualToSize(oldSize, newSize)) {
|
||||||
|
self.bounds = (CGRect){ oldBounds.origin, newSize };
|
||||||
|
|
||||||
if (! CGSizeEqualToSize(oldSize, newSize)) {
|
// Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
|
||||||
self.bounds = (CGRect){ oldBounds.origin, newSize };
|
// and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
|
||||||
|
CGPoint anchorPoint = self.anchorPoint;
|
||||||
// Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
|
CGPoint oldPosition = self.position;
|
||||||
// and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
|
CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x;
|
||||||
CGPoint anchorPoint = self.anchorPoint;
|
CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y;
|
||||||
CGPoint oldPosition = self.position;
|
self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta);
|
||||||
CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x;
|
|
||||||
CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y;
|
|
||||||
self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1685,9 +1716,11 @@ static NSInteger incrementIfFound(NSInteger i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (supernodeDidChange) {
|
if (supernodeDidChange) {
|
||||||
|
// Hierarchy state
|
||||||
ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
|
ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
|
||||||
: oldSupernode.hierarchyState);
|
: oldSupernode.hierarchyState);
|
||||||
|
|
||||||
|
// Rasterized state
|
||||||
BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.shouldRasterizeDescendants
|
BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.shouldRasterizeDescendants
|
||||||
: oldSupernode.shouldRasterizeDescendants);
|
: oldSupernode.shouldRasterizeDescendants);
|
||||||
if (parentWasOrIsRasterized) {
|
if (parentWasOrIsRasterized) {
|
||||||
@ -1696,6 +1729,10 @@ static NSInteger incrementIfFound(NSInteger i) {
|
|||||||
if (newSupernode) {
|
if (newSupernode) {
|
||||||
[self enterHierarchyState:stateToEnterOrExit];
|
[self enterHierarchyState:stateToEnterOrExit];
|
||||||
} else {
|
} else {
|
||||||
|
// If a node will be removed from the supernode it should go out from the layout pending state to remove all
|
||||||
|
// layout pending state related properties on the node
|
||||||
|
stateToEnterOrExit |= ASHierarchyStateLayoutPending;
|
||||||
|
|
||||||
[self exitHierarchyState:stateToEnterOrExit];
|
[self exitHierarchyState:stateToEnterOrExit];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2640,38 +2677,12 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
|||||||
_flags.isInHierarchy = inHierarchy;
|
_flags.isInHierarchy = inHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_hasTransitionsInProgress
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
|
||||||
return _transitionSentinel != nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_invalidateTransitionSentinel
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
|
||||||
_transitionSentinel = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_shouldAbortTransitionWithID:(int32_t)transitionID
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
|
||||||
return _transitionSentinel == nil || transitionID != _transitionSentinel.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int32_t)_newTransitionID
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
|
||||||
if (!_transitionSentinel) {
|
|
||||||
_transitionSentinel = [[ASSentinel alloc] init];
|
|
||||||
}
|
|
||||||
return [_transitionSentinel increment];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<ASLayoutable>)finalLayoutable
|
- (id<ASLayoutable>)finalLayoutable
|
||||||
{
|
{
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - ASEnvironment
|
#pragma mark - ASEnvironment
|
||||||
|
|
||||||
- (ASEnvironmentState)environmentState
|
- (ASEnvironmentState)environmentState
|
||||||
|
|||||||
@ -117,13 +117,20 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
|
|||||||
*/
|
*/
|
||||||
@property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier;
|
@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. Regardless of this properties value, progress renders will
|
||||||
|
* only occur when the node is visible. Defaults to YES.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign, readwrite) BOOL shouldRenderProgressImages;
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
#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.
|
* @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.
|
||||||
|
|
||||||
* @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below.
|
* @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below.
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, strong) PHImageManager *imageManager;
|
@property (nullable, nonatomic, strong) PHImageManager *imageManager;
|
||||||
#endif
|
#endif
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@ -85,6 +85,10 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
ASDN::RecursiveMutex _downloadIdentifierLock;
|
ASDN::RecursiveMutex _downloadIdentifierLock;
|
||||||
id _downloadIdentifier;
|
id _downloadIdentifier;
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
ASDN::RecursiveMutex _propertyLock;
|
||||||
|
BOOL _shouldRenderProgressImages;
|
||||||
|
|
||||||
//set on init only
|
//set on init only
|
||||||
BOOL _downloaderSupportsNewProtocol;
|
BOOL _downloaderSupportsNewProtocol;
|
||||||
BOOL _downloaderImplementsSetProgress;
|
BOOL _downloaderImplementsSetProgress;
|
||||||
@ -186,6 +190,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
_cacheSupportsNewProtocol = [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)];
|
_cacheSupportsNewProtocol = [cache respondsToSelector:@selector(cachedImageWithURL:callbackQueue:completion:)];
|
||||||
_cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
|
_cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
|
||||||
|
|
||||||
|
_shouldRenderProgressImages = YES;
|
||||||
|
|
||||||
self.shouldBypassEnsureDisplay = YES;
|
self.shouldBypassEnsureDisplay = YES;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -339,6 +345,27 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
#endif
|
#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 -
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
@ -436,8 +463,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Grab the best available image from the data source.
|
// Grab the best available image from the data source.
|
||||||
|
UIImage *existingImage = self.image;
|
||||||
for (id imageIdentifier in _imageIdentifiers) {
|
for (id imageIdentifier in _imageIdentifiers) {
|
||||||
UIImage *image = [_dataSource multiplexImageNode:self imageForImageIdentifier:imageIdentifier];
|
// If this image is already loaded, don't request it from the data source again because
|
||||||
|
// the data source may generate a new instance of UIImage that returns NO for isEqual:
|
||||||
|
// and we'll end up in an infinite loading loop.
|
||||||
|
UIImage *image = ASObjectIsEqual(imageIdentifier, _loadedImageIdentifier) ? existingImage : [_dataSource multiplexImageNode:self imageForImageIdentifier:imageIdentifier];
|
||||||
if (image) {
|
if (image) {
|
||||||
if (imageIdentifierOut) {
|
if (imageIdentifierOut) {
|
||||||
*imageIdentifierOut = imageIdentifier;
|
*imageIdentifierOut = imageIdentifier;
|
||||||
@ -458,32 +489,34 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
*/
|
*/
|
||||||
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
|
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
|
||||||
{
|
{
|
||||||
// Read our interface state before locking so that we don't lock super while holding our lock.
|
BOOL shouldRenderProgressImages = self.shouldRenderProgressImages;
|
||||||
ASInterfaceState interfaceState = self.interfaceState;
|
|
||||||
ASDN::MutexLocker l(_downloadIdentifierLock);
|
// Read our interface state before locking so that we don't lock super while holding our lock.
|
||||||
|
ASInterfaceState interfaceState = self.interfaceState;
|
||||||
if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASImageDownloaderProgressImage progress = nil;
|
ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock);
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
//Getting a result back for a different download identifier, download must not have been successfully canceled
|
||||||
__weak __typeof__(self) weakSelf = self;
|
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
|
||||||
progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
|
return;
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
}
|
||||||
if (strongSelf == nil) {
|
strongSelf.image = progressImage;
|
||||||
return;
|
};
|
||||||
}
|
}
|
||||||
|
[_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
|
- (void)_clearImage
|
||||||
|
|||||||
@ -73,6 +73,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic, assign, readwrite) BOOL shouldCacheImage;
|
@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. Regardless of this properties value, progress renders will
|
||||||
|
* only occur when the node is visible. 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
|
* 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.
|
* progressive progress. Calculated by dividing number of bytes / expected number of total bytes.
|
||||||
|
|||||||
@ -45,6 +45,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
BOOL _delegateSupportsDidStartFetchingData;
|
BOOL _delegateSupportsDidStartFetchingData;
|
||||||
BOOL _delegateSupportsDidFailWithError;
|
BOOL _delegateSupportsDidFailWithError;
|
||||||
BOOL _delegateSupportsImageNodeDidFinishDecoding;
|
BOOL _delegateSupportsImageNodeDidFinishDecoding;
|
||||||
|
|
||||||
|
BOOL _shouldRenderProgressImages;
|
||||||
|
|
||||||
//set on init only
|
//set on init only
|
||||||
BOOL _downloaderSupportsNewProtocol;
|
BOOL _downloaderSupportsNewProtocol;
|
||||||
@ -83,6 +85,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
|
_cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
|
||||||
|
|
||||||
_shouldCacheImage = YES;
|
_shouldCacheImage = YES;
|
||||||
|
_shouldRenderProgressImages = YES;
|
||||||
self.shouldBypassEnsureDisplay = YES;
|
self.shouldBypassEnsureDisplay = YES;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -218,6 +221,26 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
return _delegate;
|
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
|
- (BOOL)placeholderShouldPersist
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_lock);
|
ASDN::MutexLocker l(_lock);
|
||||||
@ -309,6 +332,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
*/
|
*/
|
||||||
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
|
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
|
||||||
{
|
{
|
||||||
|
BOOL shouldRenderProgressImages = self.shouldRenderProgressImages;
|
||||||
|
|
||||||
// Read our interface state before locking so that we don't lock super while holding our lock.
|
// Read our interface state before locking so that we don't lock super while holding our lock.
|
||||||
ASInterfaceState interfaceState = self.interfaceState;
|
ASInterfaceState interfaceState = self.interfaceState;
|
||||||
ASDN::MutexLocker l(_lock);
|
ASDN::MutexLocker l(_lock);
|
||||||
@ -318,7 +343,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASImageDownloaderProgressImage progress = nil;
|
ASImageDownloaderProgressImage progress = nil;
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
if (shouldRenderProgressImages && ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||||
__weak __typeof__(self) weakSelf = self;
|
__weak __typeof__(self) weakSelf = self;
|
||||||
progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
|
progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) {
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
__typeof__(self) strongSelf = weakSelf;
|
||||||
|
|||||||
@ -61,7 +61,16 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
||||||
|
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
|
||||||
|
@param downloadIdentifier The identifier for the download task that completed.
|
||||||
|
*/
|
||||||
typedef void(^ASImageDownloaderCompletion)(id <ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier);
|
typedef void(^ASImageDownloaderCompletion)(id <ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
||||||
|
*/
|
||||||
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
|
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
|
||||||
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, CGFloat progress, id _Nullable downloadIdentifier);
|
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, CGFloat progress, id _Nullable downloadIdentifier);
|
||||||
|
|
||||||
@ -98,10 +107,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
|
|||||||
@param URL The URL of the image to download.
|
@param URL The URL of the image to download.
|
||||||
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
|
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
|
||||||
@param downloadProgress The block to be invoked when the download of `URL` progresses.
|
@param downloadProgress The block to be invoked when the download of `URL` progresses.
|
||||||
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
|
||||||
@param completion The block to be invoked when the download has completed, or has failed.
|
@param completion The block to be invoked when the download has completed, or has failed.
|
||||||
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
|
||||||
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
|
|
||||||
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
||||||
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
||||||
retain the identifier if you wish to use it later.
|
retain the identifier if you wish to use it later.
|
||||||
@ -109,7 +115,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
|
|||||||
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
- (nullable id)downloadImageWithURL:(NSURL *)URL
|
||||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
||||||
completion:(nullable ASImageDownloaderCompletion)completion;
|
completion:(ASImageDownloaderCompletion)completion;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -9,8 +9,12 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "ASImageProtocols.h"
|
#import "ASImageProtocols.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
|
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
|
||||||
|
|
||||||
+ (instancetype)sharedDownloader;
|
+ (ASPINRemoteImageDownloader *)sharedDownloader;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@ -96,8 +96,9 @@
|
|||||||
|
|
||||||
- (id <ASImageContainerProtocol>)synchronouslyFetchedCachedImageWithURL:(NSURL *)URL;
|
- (id <ASImageContainerProtocol>)synchronouslyFetchedCachedImageWithURL:(NSURL *)URL;
|
||||||
{
|
{
|
||||||
NSString *key = [[self sharedPINRemoteImageManager] cacheKeyForURL:URL processorKey:nil];
|
PINRemoteImageManager *manager = [self sharedPINRemoteImageManager];
|
||||||
PINRemoteImageManagerResult *result = [[self sharedPINRemoteImageManager] synchronousImageFromCacheWithCacheKey:key options:PINRemoteImageManagerDownloadOptionsSkipDecode];
|
NSString *key = [manager cacheKeyForURL:URL processorKey:nil];
|
||||||
|
PINRemoteImageManagerResult *result = [manager synchronousImageFromCacheWithCacheKey:key options:PINRemoteImageManagerDownloadOptionsSkipDecode];
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
if (result.alternativeRepresentation) {
|
if (result.alternativeRepresentation) {
|
||||||
return result.alternativeRepresentation;
|
return result.alternativeRepresentation;
|
||||||
@ -133,7 +134,18 @@
|
|||||||
downloadProgress:(ASImageDownloaderProgress)downloadProgress
|
downloadProgress:(ASImageDownloaderProgress)downloadProgress
|
||||||
completion:(ASImageDownloaderCompletion)completion;
|
completion:(ASImageDownloaderCompletion)completion;
|
||||||
{
|
{
|
||||||
return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL options:PINRemoteImageManagerDownloadOptionsSkipDecode completion:^(PINRemoteImageManagerResult *result) {
|
return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL options:PINRemoteImageManagerDownloadOptionsSkipDecode progressDownload:^(int64_t completedBytes, int64_t totalBytes) {
|
||||||
|
if (downloadProgress == nil) { return; }
|
||||||
|
|
||||||
|
/// If we're targeting the main queue and we're on the main thread, call immediately.
|
||||||
|
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
||||||
|
downloadProgress(totalBytes / (CGFloat)completedBytes);
|
||||||
|
} else {
|
||||||
|
dispatch_async(callbackQueue, ^{
|
||||||
|
downloadProgress(totalBytes / (CGFloat)completedBytes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} completion:^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||||
/// If we're targeting the main queue and we're on the main thread, complete immediately.
|
/// If we're targeting the main queue and we're on the main thread, complete immediately.
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
#if DISPLAYNODE_USE_LOCKS
|
#if DISPLAYNODE_USE_LOCKS
|
||||||
#define _bridge_prologue_read ASDN::MutexLocker l(_propertyLock); ASDisplayNodeAssertThreadAffinity(self)
|
#define _bridge_prologue_read ASDN::MutexLocker l(_propertyLock); ASDisplayNodeAssertThreadAffinity(self)
|
||||||
#define _bridge_prologue_write ASDN::MutexLocker l(_propertyLock)
|
#define _bridge_prologue_write ASDN::MutexLocker l(_propertyLock)
|
||||||
|
#define _bridge_prologue_write_unlock ASDN::MutexUnlocker u(_propertyLock)
|
||||||
#else
|
#else
|
||||||
#define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self)
|
#define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self)
|
||||||
#define _bridge_prologue_write
|
#define _bridge_prologue_write
|
||||||
@ -331,7 +332,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
|
|||||||
// The node is loaded and we're on main.
|
// The node is loaded and we're on main.
|
||||||
// Quite the opposite of setNeedsDisplay, we must call __setNeedsLayout before messaging
|
// Quite the opposite of setNeedsDisplay, we must call __setNeedsLayout before messaging
|
||||||
// the view or layer to ensure that measurement and implicitly added subnodes have been handled.
|
// the view or layer to ensure that measurement and implicitly added subnodes have been handled.
|
||||||
|
|
||||||
|
// Calling __setNeedsLayout while holding the property lock can cause deadlocks
|
||||||
|
_bridge_prologue_write_unlock;
|
||||||
[self __setNeedsLayout];
|
[self __setNeedsLayout];
|
||||||
|
_bridge_prologue_write;
|
||||||
_messageToViewOrLayer(setNeedsLayout);
|
_messageToViewOrLayer(setNeedsLayout);
|
||||||
} else if (__loaded(self)) {
|
} else if (__loaded(self)) {
|
||||||
// The node is loaded but we're not on main.
|
// The node is loaded but we're not on main.
|
||||||
@ -341,7 +346,9 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
|
|||||||
[ASDisplayNodeGetPendingState(self) setNeedsLayout];
|
[ASDisplayNodeGetPendingState(self) setNeedsLayout];
|
||||||
} else {
|
} else {
|
||||||
// The node is not loaded and we're not on main.
|
// The node is not loaded and we're not on main.
|
||||||
|
_bridge_prologue_write_unlock;
|
||||||
[self __setNeedsLayout];
|
[self __setNeedsLayout];
|
||||||
|
_bridge_prologue_write;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -93,7 +93,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
|||||||
ASDisplayNode * __weak _supernode;
|
ASDisplayNode * __weak _supernode;
|
||||||
|
|
||||||
ASSentinel *_displaySentinel;
|
ASSentinel *_displaySentinel;
|
||||||
ASSentinel *_transitionSentinel;
|
|
||||||
|
int32_t _transitionID;
|
||||||
|
BOOL _transitionInProgress;
|
||||||
|
|
||||||
// This is the desired contentsScale, not the scale at which the layer's contents should be displayed
|
// This is the desired contentsScale, not the scale at which the layer's contents should be displayed
|
||||||
CGFloat _contentsScaleForDisplay;
|
CGFloat _contentsScaleForDisplay;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||

|

|
||||||
|
|
||||||
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
||||||
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
||||||
|
|
||||||
[](http://AsyncDisplayKit.org)
|
[](http://AsyncDisplayKit.org)
|
||||||
[](http://AsyncDisplayKit.org)
|
[](http://AsyncDisplayKit.org)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user