From 2713bdd72e2749917719500f188c46ca56d76252 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 24 Jan 2016 17:14:14 -0800 Subject: [PATCH] [ASTextNode, ASDisplayNode] Create -calculatedLayoutDidChange and use it in text node. This allows the change in size for the NSTextContainer to occur off the main thread, whenever that size change is necessary. Then the text relayout can occur earlier, during the process of computing ASLayoutSpecs. --- AsyncDisplayKit/ASDisplayNode+Subclasses.h | 7 ++++ AsyncDisplayKit/ASDisplayNode.mm | 5 +++ AsyncDisplayKit/ASTextNode.mm | 43 +++++++++++++++------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index 7d002934b5..699b6213ed 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -91,6 +91,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)layoutDidFinish; +/** + * @abstract Called on a background thread if !isNodeLoaded - called on the main thread if isNodeLoaded. + * + * @discussion When the .calculatedLayout property is set to a new ASLayout (directly from -calculateLayoutThatFits: or + * calculated via use of -layoutSpecThatFits:), subclasses may inspect it here. + */ +- (void)calculatedLayoutDidChange; /** @name Layout calculation */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index e6e435ff5e..7c88376cf8 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -594,6 +594,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _layout = [self calculateLayoutThatFits:constrainedSize]; _constrainedSize = constrainedSize; _flags.isMeasured = YES; + [self calculatedLayoutDidChange]; } ASDisplayNodeAssertTrue(_layout.layoutableObject == self); @@ -615,6 +616,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _layout; } +- (void)calculatedLayoutDidChange +{ +} + - (BOOL)displaysAsynchronously { ASDN::MutexLocker l(_propertyLock); diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index f1fc6f7eda..f4dbea3800 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -185,19 +185,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; #pragma mark - ASDisplayNode -- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize -{ - ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); - ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); - - _constrainedSize = constrainedSize; - [self _invalidateRenderer]; - ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ - [self setNeedsDisplay]; - }); - return [[self _renderer] size]; -} - // FIXME: Re-evaluate if it is still the right decision to clear the renderer at this stage. // This code was written before TextKit and when 512MB devices were still the overwhelming majority. - (void)displayDidFinish @@ -303,6 +290,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } +#pragma mark - Layout and Sizing + - (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize { if (!_renderer) { @@ -323,7 +312,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // as this would essentially serve to set its constrainedSize to be its calculatedSize (unnecessary). ASLayout *layout = self.calculatedLayout; if (layout != nil && CGSizeEqualToSize(boundsSize, layout.size)) { - if (!CGSizeEqualToSize(boundsSize, rendererConstrainedSize)) { + if (boundsSize.width != rendererConstrainedSize.width) { // Don't bother changing _constrainedSize, as ASDisplayNode's -measure: method would have a cache miss // and ask us to recalculate layout if it were called with the same calculatedSize that got us to this point! _renderer.constrainedSize = boundsSize; @@ -335,6 +324,32 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } +- (void)calculatedLayoutDidChange +{ + ASLayout *layout = self.calculatedLayout; + if (layout != nil) { + _renderer.constrainedSize = layout.size; + } +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); + ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); + + _constrainedSize = constrainedSize; + + // Instead of invalidating the renderer, in case this is a new call with a different constrained size, + // just update the size of the NSTextContainer that is owned by the renderer's internal context object. + [self _renderer].constrainedSize = _constrainedSize; + + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); + + return [[self _renderer] size]; +} + #pragma mark - Modifying User Text - (void)setAttributedString:(NSAttributedString *)attributedString