diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index ab918645c0..8e72650b35 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -650,10 +650,15 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) } } - // only calculate the size if - // - we haven't already - // - the constrained size range is different - return (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _layout.constrainedSizeRange)); + // Only generate a new layout if: + // - The current layout is dirty + // - The passed constrained size is different than the layout's constrained size + return ([self _dirtyLayout] || !ASSizeRangeEqualToSizeRange(constrainedSize, _layout.constrainedSizeRange)); +} + +- (BOOL)_dirtyLayout +{ + return _layout == nil || _layout.isDirty; } #pragma mark - Layout Transition @@ -1019,7 +1024,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssertThreadAffinity(self); ASDN::MutexLocker l(_propertyLock); - if (!_flags.isMeasured) { + if ([self _dirtyLayout]) { return; } @@ -1095,7 +1100,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Normally measure will be called before layout occurs. If this doesn't happen, nothing is going to call it at all. // We simply call measureWithSizeRange: using a size range equal to whatever bounds were provided to that element or // try to measure the node with the largest size as possible - if (self.supernode == nil && !self.supportsRangeManagedInterfaceState && !_flags.isMeasured) { + if (self.supernode == nil && !self.supportsRangeManagedInterfaceState && [self _dirtyLayout] == NO) { if (CGRectEqualToRect(bounds, CGRectZero)) { LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self); } else { @@ -1108,7 +1113,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { ASDisplayNodeAssertMainThread(); - if (!_flags.isMeasured) { + if ([self _dirtyLayout]) { return; } @@ -2058,8 +2063,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)invalidateCalculatedLayout { ASDN::MutexLocker l(_propertyLock); - // This will cause -measureWithSizeRange: to actually compute the size instead of returning the previously cached size - _flags.isMeasured = NO; + + // This will cause the next call to -measureWithSizeRange: to actually compute a new layout + // instead of returning the current layout + _layout.dirty = YES; } - (void)__didLoad @@ -2395,8 +2402,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) } } -- (void)applyLayout:(ASLayout *)layout - layoutContext:(ASLayoutTransition *)layoutContext +- (void)applyLayout:(ASLayout *)layout layoutContext:(ASLayoutTransition *)layoutContext { ASDN::MutexLocker l(_propertyLock); _layout = layout; @@ -2404,8 +2410,6 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) ASDisplayNodeAssertTrue(layout.layoutableObject == self); ASDisplayNodeAssertTrue(layout.size.width >= 0.0); ASDisplayNodeAssertTrue(layout.size.height >= 0.0); - - _flags.isMeasured = YES; if (self.usesImplicitHierarchyManagement && layoutContext != nil) { [layoutContext applySubnodeInsertions]; diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index cc1e21f8b0..7733a6a207 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -21,7 +21,9 @@ extern CGPoint const CGPointNull; extern BOOL CGPointIsNull(CGPoint point); -/** Represents a computed immutable layout tree. */ +/** + * A node in the layout tree that represents the size and position of the object that created it (ASLayoutable). + */ @interface ASLayout : NSObject /** @@ -56,6 +58,11 @@ extern BOOL CGPointIsNull(CGPoint point); */ @property (nonatomic, readonly) NSArray *immediateSublayouts; +/** + * Mark the layout dirty for future regeneration. + */ +@property (nonatomic, getter=isDirty) BOOL dirty; + /** * A boolean describing if the current layout has been flattened. */ diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index b87e8f9393..007e92fa00 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -50,6 +50,7 @@ extern BOOL CGPointIsNull(CGPoint point) } l->_constrainedSizeRange = sizeRange; l->_size = size; + l->_dirty = NO; if (CGPointIsNull(position) == NO) { l->_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 1e5efee2dc..4e1fa9524b 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -82,7 +82,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo unsigned implementsDrawParameters:1; // internal state - unsigned isMeasured:1; unsigned isEnteringHierarchy:1; unsigned isExitingHierarchy:1; unsigned isInHierarchy:1;