Merge remote-tracking branch 'facebook/master' into ASVideoPlayerNode

This commit is contained in:
Erekle 2016-05-10 20:19:13 +04:00
commit b09d0dca9e
12 changed files with 213 additions and 95 deletions

View File

@ -125,6 +125,10 @@
{
CGSize oldSize = self.calculatedSize;
[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];
}

View File

@ -373,7 +373,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_pendingViewState = nil;
_displaySentinel = nil;
_transitionSentinel = nil;
_pendingDisplayNodes = nil;
}
@ -654,6 +653,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize));
}
#pragma mark - Layout Transition
- (void)transitionLayoutWithAnimation:(BOOL)animated
shouldMeasureAsync:(BOOL)shouldMeasureAsync
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.");
}
int32_t transitionID = [self _newTransitionID];
int32_t transitionID = [self _startNewTransition];
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.pendingTransitionID = transitionID;
});
@ -726,13 +727,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASSizeRange previousConstrainedSize = _constrainedSize;
[self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:nil];
[self _invalidateTransitionSentinel];
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
[node applyPendingLayoutContext];
[node _completeLayoutCalculation];
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
});
[self _finishOrCancelTransition];
if (completion) {
completion();
@ -781,7 +782,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}
}
- (void)calculatedLayoutDidChange
{
// subclass override
@ -790,9 +790,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
- (void)cancelLayoutTransitionsInProgress
{
ASDN::MutexLocker l(_propertyLock);
if ([self _hasTransitionsInProgress]) {
// Invalidate transition sentinel to cancel transitions in progress
[self _invalidateTransitionSentinel];
if ([self _hasTransitionInProgress]) {
// Cancel transition in progress
[self _finishOrCancelTransition];
// Tell subnodes to exit layout pending state and clear related properties
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
@ -800,8 +801,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}
}
#pragma mark - Layout Transition
- (BOOL)usesImplicitHierarchyManagement
{
ASDN::MutexLocker l(_propertyLock);
@ -814,6 +813,33 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_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
{
[self __layoutSublayouts];
@ -827,6 +853,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_pendingLayoutContext = nil;
}
#pragma mark - _ASTransitionContextCompletionDelegate
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
@ -1001,27 +1028,31 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self invalidateCalculatedLayout];
if (_supernode) {
ASDisplayNode *supernode = _supernode;
ASDN::MutexUnlocker u(_propertyLock);
// Cause supernode's layout to be invalidated
[_supernode setNeedsLayout];
} else {
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
[self measureWithSizeRange:oldConstrainedSize];
// We need to release the lock to prevent a deadlock
[supernode setNeedsLayout];
return;
}
// 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;
CGSize oldSize = oldBounds.size;
CGSize newSize = _layout.size;
CGRect oldBounds = self.bounds;
CGSize oldSize = oldBounds.size;
CGSize newSize = _layout.size;
if (! CGSizeEqualToSize(oldSize, newSize)) {
self.bounds = (CGRect){ oldBounds.origin, newSize };
if (! CGSizeEqualToSize(oldSize, newSize)) {
self.bounds = (CGRect){ oldBounds.origin, newSize };
// Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
// and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
CGPoint anchorPoint = self.anchorPoint;
CGPoint oldPosition = self.position;
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);
}
// Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
// and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
CGPoint anchorPoint = self.anchorPoint;
CGPoint oldPosition = self.position;
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) {
// Hierarchy state
ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
: oldSupernode.hierarchyState);
// Rasterized state
BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.shouldRasterizeDescendants
: oldSupernode.shouldRasterizeDescendants);
if (parentWasOrIsRasterized) {
@ -1696,6 +1729,10 @@ static NSInteger incrementIfFound(NSInteger i) {
if (newSupernode) {
[self enterHierarchyState:stateToEnterOrExit];
} 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];
}
}
@ -2640,38 +2677,12 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
_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
{
return self;
}
#pragma mark - ASEnvironment
- (ASEnvironmentState)environmentState

View File

@ -117,13 +117,20 @@ 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. 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
/**
* @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.
*/
@property (nonatomic, strong) PHImageManager *imageManager;
@property (nullable, nonatomic, strong) PHImageManager *imageManager;
#endif
@end

View File

@ -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 -
@ -436,8 +463,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}
// Grab the best available image from the data source.
UIImage *existingImage = self.image;
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 (imageIdentifierOut) {
*imageIdentifierOut = imageIdentifier;
@ -458,32 +489,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

View File

@ -73,6 +73,13 @@ 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. 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
* progressive progress. Calculated by dividing number of bytes / expected number of total bytes.

View File

@ -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;

View File

@ -61,7 +61,16 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
@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);
/**
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
*/
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
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 callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
@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 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.
@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.
@ -109,7 +115,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
- (nullable id)downloadImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
completion:(nullable ASImageDownloaderCompletion)completion;
completion:(ASImageDownloaderCompletion)completion;
/**

View File

@ -9,8 +9,12 @@
#import <Foundation/Foundation.h>
#import "ASImageProtocols.h"
NS_ASSUME_NONNULL_BEGIN
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
+ (instancetype)sharedDownloader;
+ (ASPINRemoteImageDownloader *)sharedDownloader;
@end
NS_ASSUME_NONNULL_END

View File

@ -96,8 +96,9 @@
- (id <ASImageContainerProtocol>)synchronouslyFetchedCachedImageWithURL:(NSURL *)URL;
{
NSString *key = [[self sharedPINRemoteImageManager] cacheKeyForURL:URL processorKey:nil];
PINRemoteImageManagerResult *result = [[self sharedPINRemoteImageManager] synchronousImageFromCacheWithCacheKey:key options:PINRemoteImageManagerDownloadOptionsSkipDecode];
PINRemoteImageManager *manager = [self sharedPINRemoteImageManager];
NSString *key = [manager cacheKeyForURL:URL processorKey:nil];
PINRemoteImageManagerResult *result = [manager synchronousImageFromCacheWithCacheKey:key options:PINRemoteImageManagerDownloadOptionsSkipDecode];
#if PIN_ANIMATED_AVAILABLE
if (result.alternativeRepresentation) {
return result.alternativeRepresentation;
@ -133,7 +134,18 @@
downloadProgress:(ASImageDownloaderProgress)downloadProgress
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 (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
#if PIN_ANIMATED_AVAILABLE

View File

@ -37,6 +37,7 @@
#if DISPLAYNODE_USE_LOCKS
#define _bridge_prologue_read ASDN::MutexLocker l(_propertyLock); ASDisplayNodeAssertThreadAffinity(self)
#define _bridge_prologue_write ASDN::MutexLocker l(_propertyLock)
#define _bridge_prologue_write_unlock ASDN::MutexUnlocker u(_propertyLock)
#else
#define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self)
#define _bridge_prologue_write
@ -331,7 +332,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
// The node is loaded and we're on main.
// 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.
// Calling __setNeedsLayout while holding the property lock can cause deadlocks
_bridge_prologue_write_unlock;
[self __setNeedsLayout];
_bridge_prologue_write;
_messageToViewOrLayer(setNeedsLayout);
} else if (__loaded(self)) {
// The node is loaded but we're not on main.
@ -341,7 +346,9 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
[ASDisplayNodeGetPendingState(self) setNeedsLayout];
} else {
// The node is not loaded and we're not on main.
_bridge_prologue_write_unlock;
[self __setNeedsLayout];
_bridge_prologue_write;
}
}

View File

@ -93,7 +93,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
ASDisplayNode * __weak _supernode;
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
CGFloat _contentsScaleForDisplay;

View File

@ -1,7 +1,7 @@
![AsyncDisplayKit](https://github.com/facebook/AsyncDisplayKit/blob/master/docs/assets/logo.png)
[![Apps Using](https://img.shields.io/badge/Apps%20Using%20ASDK-%3E3,178-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Downloads](https://img.shields.io/badge/Total%20Downloads-%3E336,372-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Apps Using](https://img.shields.io/badge/Apps%20Using%20ASDK-%3E3,658-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Downloads](https://img.shields.io/badge/Total%20Downloads-%3E377,749-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://AsyncDisplayKit.org)
[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://AsyncDisplayKit.org)