diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 9eb503a13e..5539a95116 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -50,6 +50,77 @@ struct ASTextNodeDrawParameter { UIColor *backgroundColor; }; +#pragma mark - ASTextKitRenderer + +// Not used at the moment but handy to have +/*ASDISPLAYNODE_INLINE NSUInteger ASHashFromCGRect(CGRect rect) +{ + return ((*(NSUInteger *)&rect.origin.x << 10 ^ *(NSUInteger *)&rect.origin.y) + (*(NSUInteger *)&rect.size.width << 10 ^ *(NSUInteger *)&rect.size.height)); +}*/ + +ASDISPLAYNODE_INLINE NSUInteger ASHashFromCGSize(CGSize size) +{ + return ((*(NSUInteger *)&size.width << 10 ^ *(NSUInteger *)&size.height)); +} + +@interface ASTextNodeRendererKey : NSObject +@property (assign, nonatomic) ASTextKitAttributes attributes; +@property (assign, nonatomic) CGSize constrainedSize; +@end + +@implementation ASTextNodeRendererKey + +- (NSUInteger)hash +{ + return _attributes.hash() ^ ASHashFromCGSize(_constrainedSize); +} + +- (BOOL)isEqual:(ASTextNodeRendererKey *)object +{ + if (self == object) { + return YES; + } + + return _attributes.hash() == object.attributes.hash() + && CGSizeEqualToSize(_constrainedSize, object.constrainedSize); +} + +@end + +static NSCache *sharedRendererCache() +{ + static dispatch_once_t onceToken; + static NSCache *__rendererCache = nil; + dispatch_once(&onceToken, ^{ + __rendererCache = [[NSCache alloc] init]; + __rendererCache.countLimit = 500; // 500 renders cache + }); + return __rendererCache; +} + +/** + The concept here is that neither the node nor layout should ever have a strong reference to the renderer object. + This is to reduce memory load when loading thousands and thousands of text nodes into memory at once. Instead + we maintain a LRU renderer cache that is queried via a unique key based on text kit attributes and constrained size. + */ + +static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, CGSize constrainedSize) +{ + NSCache *cache = sharedRendererCache(); + + ASTextNodeRendererKey *key = [[ASTextNodeRendererKey alloc] init]; + key.attributes = attributes; + key.constrainedSize = constrainedSize; + + ASTextKitRenderer *renderer = [cache objectForKey:key]; + if (renderer == nil) { + renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:attributes constrainedSize:constrainedSize]; + [cache setObject:renderer forKey:key]; + } + + return renderer; +} + @interface ASTextNode () @end @@ -73,10 +144,6 @@ struct ASTextNodeDrawParameter { NSRange _highlightRange; ASHighlightOverlayLayer *_activeHighlightLayer; - CGSize _constrainedSize; - - ASTextKitRenderer *_renderer; - ASTextNodeDrawParameter _drawParameter; UILongPressGestureRecognizer *_longPressGestureRecognizer; @@ -123,8 +190,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; self.isAccessibilityElement = YES; self.accessibilityTraits = UIAccessibilityTraitStaticText; - _constrainedSize = CGSizeMake(-INFINITY, -INFINITY); - // Placeholders // Disabled by default in ASDisplayNode, but add a few options for those who toggle // on the special placeholder behavior of ASTextNode. @@ -139,8 +204,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; { CGColorRelease(_shadowColor); - [self _invalidateRenderer]; - if (_longPressGestureRecognizer) { _longPressGestureRecognizer.delegate = nil; [_longPressGestureRecognizer removeTarget:nil action:NULL]; @@ -181,27 +244,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; #pragma mark - ASDisplayNode -// 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 -{ - [super displayDidFinish]; - - // We invalidate our renderer here to clear the very high memory cost of - // keeping this around. _invalidateRenderer will dealloc this onto a bg - // thread resulting in less stutters on the main thread than if it were - // to be deallocated in dealloc. This is also helpful in opportunistically - // reducing memory consumption and reducing the overall footprint of the app. - [self _invalidateRenderer]; -} - - (void)clearContents { // We discard the backing store and renderer to prevent the very large // memory overhead of maintaining these for all text nodes. They can be // regenerated when layout is necessary. [super clearContents]; // ASDisplayNode will set layer.contents = nil - [self _invalidateRenderer]; } - (void)didLoad @@ -218,45 +266,23 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } -- (void)setFrame:(CGRect)frame -{ - [super setFrame:frame]; - [self _invalidateRendererIfNeededForBoundsSize:frame.size]; -} - -- (void)setBounds:(CGRect)bounds -{ - [super setBounds:bounds]; - [self _invalidateRendererIfNeededForBoundsSize:bounds.size]; -} - #pragma mark - Renderer Management - (ASTextKitRenderer *)_renderer { - return [self _rendererWithBounds:self.threadSafeBounds]; + CGSize constrainedSize = self.threadSafeBounds.size; + return [self _rendererWithBoundsSlow:{.size = constrainedSize}]; } -- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds +- (ASTextKitRenderer *)_rendererWithBoundsSlow:(CGRect)bounds { ASDN::MutexLocker l(__instanceLock__); - - if (_renderer == nil) { - CGSize constrainedSize; - if (_constrainedSize.width != -INFINITY) { - constrainedSize = _constrainedSize; - } else { - constrainedSize = bounds.size; - constrainedSize.width -= (_textContainerInset.left + _textContainerInset.right); - constrainedSize.height -= (_textContainerInset.top + _textContainerInset.bottom); - } - - _renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes] - constrainedSize:constrainedSize]; - } - return _renderer; + bounds.size.width -= (_textContainerInset.left + _textContainerInset.right); + bounds.size.height -= (_textContainerInset.top + _textContainerInset.bottom); + return rendererForAttributes([self _rendererAttributes], bounds.size); } + - (ASTextKitAttributes)_rendererAttributes { ASDN::MutexLocker l(__instanceLock__); @@ -276,38 +302,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; }; } -- (void)_invalidateRendererIfNeeded -{ - [self _invalidateRendererIfNeededForBoundsSize:self.threadSafeBounds.size]; -} - -- (void)_invalidateRendererIfNeededForBoundsSize:(CGSize)boundsSize -{ - if ([self _needInvalidateRendererForBoundsSize:boundsSize]) { - // Our bounds have changed to a size that is not identical to our constraining size, - // so our previous layout information is invalid, and TextKit may draw at the - // incorrect origin. - { - ASDN::MutexLocker l(__instanceLock__); - _constrainedSize = CGSizeMake(-INFINITY, -INFINITY); - } - [self _invalidateRenderer]; - } -} - -- (void)_invalidateRenderer -{ - ASDN::MutexLocker l(__instanceLock__); - - if (_renderer) { - // Destruction of the layout managers/containers/text storage is quite - // expensive, and can take some time, so we dispatch onto a bg queue to - // actually dealloc. - ASPerformBackgroundDeallocation(_renderer); - _renderer = nil; - } -} - #pragma mark - Layout and Sizing - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset @@ -327,60 +321,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; return _textContainerInset; } -- (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize -{ - ASDN::MutexLocker l(__instanceLock__); - - if (_renderer == nil) { - return YES; - } - - // If the size is not the same as the constraint we provided to the renderer, start out assuming we need - // a new one. However, there are common cases where the constrained size doesn't need to be the same as calculated. - CGSize rendererConstrainedSize = _renderer.constrainedSize; - - //inset bounds - boundsSize.width -= _textContainerInset.left + _textContainerInset.right; - boundsSize.height -= _textContainerInset.top + _textContainerInset.bottom; - - if (CGSizeEqualToSize(boundsSize, rendererConstrainedSize)) { - return NO; - } else { - // It is very common to have a constrainedSize with a concrete, specific width but +Inf height. - // In this case, as long as the text node has bounds as large as the full calculatedLayout suggests, - // it means that the text has all the room it needs (as it was not vertically bounded). So, we will not - // experience truncation and don't need to recreate the renderer with the size it already calculated, - // 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)) { - return (boundsSize.width != rendererConstrainedSize.width); - } else { - return YES; - } - } -} - -- (void)calculatedLayoutDidChange -{ - [super calculatedLayoutDidChange]; - - ASLayout *layout = self.calculatedLayout; - - if (layout != nil) { - ASDN::MutexLocker l(__instanceLock__); - CGSize layoutSize = layout.size; - - // Apply textContainerInset - layoutSize.width -= (_textContainerInset.left + _textContainerInset.right); - layoutSize.height -= (_textContainerInset.top + _textContainerInset.bottom); - - if (CGSizeEqualToSize(_constrainedSize, layoutSize) == NO) { - _constrainedSize = layoutSize; - [self _invalidateRenderer]; - } - } -} - - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDN::MutexLocker l(__instanceLock__); @@ -390,27 +330,18 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Cache the original constrained size for final size calculateion CGSize originalConstrainedSize = constrainedSize; - - // Adjust constrainedSize for textContainerInset before assigning it - constrainedSize.width -= (_textContainerInset.left + _textContainerInset.right); - constrainedSize.height -= (_textContainerInset.top + _textContainerInset.bottom); - - _constrainedSize = constrainedSize; - - if (_renderer != nil && CGSizeEqualToSize(constrainedSize, _renderer.constrainedSize) == NO) { - [self _invalidateRenderer]; - } [self setNeedsDisplay]; - CGSize size = [self _renderer].size; + ASTextKitRenderer *renderer = [self _rendererWithBoundsSlow:{.size = constrainedSize}]; + CGSize size = renderer.size; if (_attributedText.length > 0) { self.style.ascender = [[self class] ascenderWithAttributedString:_attributedText]; self.style.descender = [[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender]; - if (_renderer.currentScaleFactor > 0 && _renderer.currentScaleFactor < 1.0) { + if (renderer.currentScaleFactor > 0 && renderer.currentScaleFactor < 1.0) { // while not perfect, this is a good estimate of what the ascender of the scaled font will be. - self.style.ascender *= _renderer.currentScaleFactor; - self.style.descender *= _renderer.currentScaleFactor; + self.style.ascender *= renderer.currentScaleFactor; + self.style.descender *= renderer.currentScaleFactor; } } @@ -461,9 +392,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Without this, the size calculation of the text with truncation applied will // not take into account the attributes of attributedText in the last line [self _updateComposedTruncationText]; - - // We need an entirely new renderer - [self _invalidateRenderer]; } NSUInteger length = attributedText.length; @@ -495,7 +423,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } _exclusionPaths = [exclusionPaths copy]; - [self _invalidateRenderer]; [self setNeedsLayout]; [self setNeedsDisplay]; } @@ -536,7 +463,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; CGContextTranslateCTM(context, _textContainerInset.left, _textContainerInset.top); - ASTextKitRenderer *renderer = [self _rendererWithBounds:drawParameterBounds]; + ASTextKitRenderer *renderer = [self _rendererWithBoundsSlow:drawParameterBounds]; // Fill background if (backgroundColor != nil) { @@ -790,11 +717,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; if (highlightTargetLayer != nil) { ASDN::MutexLocker l(__instanceLock__); + ASTextKitRenderer *renderer = [self _renderer]; - NSArray *highlightRects = [[self _renderer] rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock]; + NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock]; NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count]; for (NSValue *rectValue in highlightRects) { - UIEdgeInsets shadowPadding = _renderer.shadower.shadowPadding; + UIEdgeInsets shadowPadding = renderer.shadower.shadowPadding; CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.CGRectValue, shadowPadding); // The rects returned from renderer don't have `textContainerInset`, @@ -1119,7 +1047,6 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI CGColorRelease(_shadowColor); _shadowColor = CGColorRetain(shadowColor); _cachedShadowUIColor = [UIColor colorWithCGColor:shadowColor]; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1137,7 +1064,6 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) { _shadowOffset = shadowOffset; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1155,7 +1081,6 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI if (_shadowOpacity != shadowOpacity) { _shadowOpacity = shadowOpacity; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1173,7 +1098,6 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI if (_shadowRadius != shadowRadius) { _shadowRadius = shadowRadius; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1232,7 +1156,6 @@ static NSAttributedString *DefaultTruncationAttributedString() if (_truncationMode != truncationMode) { _truncationMode = truncationMode; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1251,7 +1174,6 @@ static NSAttributedString *DefaultTruncationAttributedString() if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) { _pointSizeScaleFactors = pointSizeScaleFactors; - [self _invalidateRenderer]; [self setNeedsDisplay]; }} @@ -1261,7 +1183,6 @@ static NSAttributedString *DefaultTruncationAttributedString() if (_maximumNumberOfLines != maximumNumberOfLines) { _maximumNumberOfLines = maximumNumberOfLines; - [self _invalidateRenderer]; [self setNeedsDisplay]; } } @@ -1285,7 +1206,6 @@ static NSAttributedString *DefaultTruncationAttributedString() - (void)_invalidateTruncationText { [self _updateComposedTruncationText]; - [self _invalidateRenderer]; [self setNeedsDisplay]; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h index 0021ec94cb..5b77fcfc52 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h +++ b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h @@ -110,7 +110,8 @@ struct ASTextKitAttributes { && maximumNumberOfLines == other.maximumNumberOfLines && shadowOpacity == other.shadowOpacity && shadowRadius == other.shadowRadius - && [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors] + && (pointSizeScaleFactors == other.pointSizeScaleFactors + || [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors]) && CGSizeEqualToSize(shadowOffset, other.shadowOffset) && ASObjectIsEqual(exclusionPaths, other.exclusionPaths) && ASObjectIsEqual(avoidTailTruncationSet, other.avoidTailTruncationSet) diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.h b/AsyncDisplayKit/TextKit/ASTextKitContext.h index 58257efbab..9e90fac710 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.h +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.h @@ -29,8 +29,6 @@ exclusionPaths:(NSArray *)exclusionPaths constrainedSize:(CGSize)constrainedSize; -@property (nonatomic, assign, readwrite) CGSize constrainedSize; - /** All operations on TextKit values MUST occur within this locked context. Simultaneous access (even non-mutative) to TextKit components may cause crashes. diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.mm b/AsyncDisplayKit/TextKit/ASTextKitContext.mm index ba7477c500..7eca01d531 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.mm @@ -54,18 +54,6 @@ return self; } -- (CGSize)constrainedSize -{ - ASDN::MutexSharedLocker l(__instanceLock__); - return _textContainer.size; -} - -- (void)setConstrainedSize:(CGSize)constrainedSize -{ - ASDN::MutexSharedLocker l(__instanceLock__); - _textContainer.size = constrainedSize; -} - - (void)performBlockWithLockedTextKitComponents:(void (^)(NSLayoutManager *, NSTextStorage *, NSTextContainer *))block diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index d4246b2a1c..1967b98264 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -37,7 +37,6 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() @implementation ASTextKitRenderer { CGSize _calculatedSize; - BOOL _sizeIsCalculated; } @synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater, fontSizeAdjuster = _fontSizeAdjuster; @@ -49,62 +48,38 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() if (self = [super init]) { _constrainedSize = constrainedSize; _attributes = attributes; - _sizeIsCalculated = NO; _currentScaleFactor = 1; - } - return self; -} - -- (ASTextKitShadower *)shadower -{ - if (!_shadower) { - ASTextKitAttributes attributes = _attributes; + + // As the renderer should be thread safe, create all subcomponents in the initialization method _shadower = [ASTextKitShadower shadowerWithShadowOffset:attributes.shadowOffset shadowColor:attributes.shadowColor shadowOpacity:attributes.shadowOpacity shadowRadius:attributes.shadowRadius]; - } - return _shadower; -} - -- (ASTextKitTailTruncater *)truncater -{ - if (!_truncater) { - ASTextKitAttributes attributes = _attributes; - NSCharacterSet *avoidTailTruncationSet = attributes.avoidTailTruncationSet ? : _defaultAvoidTruncationCharacterSet(); - _truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context] - truncationAttributedString:attributes.truncationAttributedString - avoidTailTruncationSet:avoidTailTruncationSet]; - } - return _truncater; -} - -- (ASTextKitFontSizeAdjuster *)fontSizeAdjuster -{ - if (!_fontSizeAdjuster) { - ASTextKitAttributes attributes = _attributes; - // We must inset the constrained size by the size of the shadower. - CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize]; - _fontSizeAdjuster = [[ASTextKitFontSizeAdjuster alloc] initWithContext:[self context] - constrainedSize:shadowConstrainedSize - textKitAttributes:attributes]; - } - return _fontSizeAdjuster; -} - -- (ASTextKitContext *)context -{ - if (!_context) { - ASTextKitAttributes attributes = _attributes; + // We must inset the constrained size by the size of the shadower. CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize]; + _context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString lineBreakMode:attributes.lineBreakMode maximumNumberOfLines:attributes.maximumNumberOfLines exclusionPaths:attributes.exclusionPaths constrainedSize:shadowConstrainedSize]; + + NSCharacterSet *avoidTailTruncationSet = attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet(); + _truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context] + truncationAttributedString:attributes.truncationAttributedString + avoidTailTruncationSet:avoidTailTruncationSet]; + + ASTextKitAttributes attributes = _attributes; + // We must inset the constrained size by the size of the shadower. + _fontSizeAdjuster = [[ASTextKitFontSizeAdjuster alloc] initWithContext:[self context] + constrainedSize:shadowConstrainedSize + textKitAttributes:attributes]; + + // Calcualate size immediately + [self _calculateSize]; } - return _context; + return self; } - (NSStringDrawingContext *)stringDrawingContext @@ -127,10 +102,6 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() - (CGSize)size { - if (!_sizeIsCalculated) { - [self _calculateSize]; - _sizeIsCalculated = YES; - } return _calculatedSize; } @@ -222,12 +193,6 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() { // We add an assertion so we can track the rare conditions where a graphics context is not present ASDisplayNodeAssertNotNil(context, @"This is no good without a context."); - - // This renderer may not be the one that did the sizing. If that is the case its truncation and currentScaleFactor may not have been evaluated. - // If there's any possibility we need to truncate or scale (i.e. width is not infinite), perform the size calculation. - if (_sizeIsCalculated == NO && isinf(_constrainedSize.width) == NO) { - [self _calculateSize]; - } bounds = CGRectIntersection(bounds, { .size = _constrainedSize }); CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds]; @@ -298,9 +263,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() - (std::vector)visibleRanges { - ASTextKitTailTruncater *truncater = [self truncater]; - [truncater truncate]; - return truncater.visibleRanges; + return _truncater.visibleRanges; } @end diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m index 2a2a1b704d..c0acf7ad4c 100644 --- a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m @@ -18,6 +18,13 @@ @implementation ASTextNodeSnapshotTests +- (void)setUp +{ + [super setUp]; + + self.recordMode = NO; +} + - (void)testTextContainerInset { // trivial test case to ensure ASSnapshotTestCase works diff --git a/AsyncDisplayKitTests/ASTextNodeTests.m b/AsyncDisplayKitTests/ASTextNodeTests.m index ff1620cd72..7395e15b3b 100644 --- a/AsyncDisplayKitTests/ASTextNodeTests.m +++ b/AsyncDisplayKitTests/ASTextNodeTests.m @@ -171,7 +171,9 @@ ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new]; _textNode.delegate = delegate; - [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))]; + ASLayout *layout = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))]; + _textNode.frame = CGRectMake(0, 0, layout.size.width, layout.size.height); + NSRange returnedLinkRange; NSString *returnedAttributeName; NSString *returnedLinkAttributeValue = [_textNode linkAttributeValueAtPoint:CGPointMake(3, 3) attributeName:&returnedAttributeName range:&returnedLinkRange]; diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png index 4f8364937f..65c801d4df 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png differ