From f7a4ca45db1e892d14e73e876afab1741a5f69c4 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 11:31:10 -0700 Subject: [PATCH 1/4] Fix ASTextNodeWordKernerTests for Xcode 7/iOS 9 --- AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm b/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm index dc683fd677..4768197ce0 100644 --- a/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm +++ b/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm @@ -80,7 +80,7 @@ CGFloat expectedWidth = [@" " sizeWithAttributes:@{ NSFontAttributeName : font }].width; CGRect boundingBox = [_layoutManagerDelegate layoutManager:_components.layoutManager boundingBoxForControlGlyphAtIndex:0 forTextContainer:_components.textContainer proposedLineFragment:CGRectZero glyphPosition:CGPointZero characterIndex:0]; - + XCTAssertEqualWithAccuracy(boundingBox.size.width, expectedWidth, FLT_EPSILON, @"Word kerning shouldn't alter the default width of %f. Encountered space width was %f", expectedWidth, boundingBox.size.width); } @@ -111,7 +111,7 @@ } NSGlyphProperty *glyphProperties = (NSGlyphProperty *)malloc(sizeof(NSGlyphProperty) * glyphCount); CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * glyphCount); - NSInteger glyphsToChange = [_layoutManagerDelegate layoutManager:_components.layoutManager shouldGenerateGlyphs:glyphs properties:glyphProperties characterIndexes:characterIndexes font:NULL forGlyphRange:stringRange]; + NSInteger glyphsToChange = [_layoutManagerDelegate layoutManager:_components.layoutManager shouldGenerateGlyphs:glyphs properties:glyphProperties characterIndexes:characterIndexes font:[UIFont systemFontOfSize:12.0] forGlyphRange:stringRange]; free(characterIndexes); free(glyphProperties); free(glyphs); From 15133fdbf73a0489bed14c7982ae9346b25e38c4 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 12:11:19 -0700 Subject: [PATCH 2/4] setNeedsDisplay must be called on main --- AsyncDisplayKit/ASDisplayNode.mm | 16 +++++ AsyncDisplayKit/ASTextNode.mm | 59 +++++++++++++------ .../Private/ASDisplayNodeInternal.h | 1 + Podfile.lock | 2 +- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 43dbc037fa..8d2566cef8 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -64,6 +64,22 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) } } +void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()) +{ + ASDisplayNodeCAssertNotNil(block, @"block is required"); + if (!block) { + return; + } + + if (node.nodeLoaded) { + ASDisplayNodePerformBlockOnMainThread(^{ + block(); + }); + } else { + block(); + } +} + + (void)initialize { if (self == [ASDisplayNode class]) { diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 0206040c07..e5d4ac2348 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import @@ -202,7 +203,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) _constrainedSize = constrainedSizeForText; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); CGSize rendererSize = [[self _renderer] size]; // Add shadow padding back @@ -337,19 +340,21 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) // We need an entirely new renderer [self _invalidateRenderer]; - // Tell the display node superclasses that the cached layout is incorrect now - [self invalidateCalculatedLayout]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + // Tell the display node superclasses that the cached layout is incorrect now + [self invalidateCalculatedLayout]; - [self setNeedsDisplay]; + [self setNeedsDisplay]; - self.accessibilityLabel = _attributedString.string; + self.accessibilityLabel = _attributedString.string; - if (_attributedString.length == 0) { - // We're not an accessibility element by default if there is no string. - self.isAccessibilityElement = NO; - } else { - self.isAccessibilityElement = YES; - } + if (_attributedString.length == 0) { + // We're not an accessibility element by default if there is no string. + self.isAccessibilityElement = NO; + } else { + self.isAccessibilityElement = YES; + } + }); } #pragma mark - Text Layout @@ -360,7 +365,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) _exclusionPaths = exclusionPaths; [self _invalidateRenderer]; [self invalidateCalculatedLayout]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -897,7 +904,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } _shadowColor = shadowColor; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -911,7 +920,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) { _shadowOffset = shadowOffset; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -925,7 +936,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_shadowOpacity != shadowOpacity) { _shadowOpacity = shadowOpacity; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -939,7 +952,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_shadowRadius != shadowRadius) { _shadowRadius = shadowRadius; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -981,7 +996,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_truncationMode != truncationMode) { _truncationMode = truncationMode; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -994,8 +1011,10 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) { if (_maximumLineCount != maximumLineCount) { _maximumLineCount = maximumLineCount; - [self _invalidateRenderer]; + [self _invalidateRenderer]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ [self setNeedsDisplay]; + }); } } @@ -1010,7 +1029,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) { _composedTruncationString = [self _prepareTruncationStringForDrawing:[self _composedTruncationString]]; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } /** diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 39ce70105b..b3df379c26 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -20,6 +20,7 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); +void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()); typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { ASDisplayNodeMethodOverrideNone = 0, diff --git a/Podfile.lock b/Podfile.lock index 423c2d2851..cf8e89d790 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.37.2 +COCOAPODS: 0.38.2 From d5de2a22fd6044e2d30b37d8184a8a15e6deb085 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 12:24:04 -0700 Subject: [PATCH 3/4] Text node to gracefully fail upon font failure --- AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m b/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m index 3ea9d596f2..55efebdce8 100644 --- a/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m +++ b/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m @@ -11,6 +11,8 @@ #import #import +#import "ASAssert.h" + #pragma mark - Public BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName) { @@ -65,8 +67,13 @@ NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *co CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue; NSString *fontName = (__bridge_transfer NSString *)CTFontCopyPostScriptName(coreTextFont); CGFloat fontSize = CTFontGetSize(coreTextFont); - - cleanAttributes[NSFontAttributeName] = [UIFont fontWithName:fontName size:fontSize]; + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + ASDisplayNodeCAssertNotNil(font, @"unable to load font %@ with size %f", fontName, fontSize); + if (font == nil) { + // Gracefully fail if we were unable to load the font. + font = [UIFont systemFontOfSize:fontSize]; + } + cleanAttributes[NSFontAttributeName] = font; } // kCTKernAttributeName -> NSKernAttributeName else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) { From 6275219f7902068f13589d7043f4cb6ff150069c Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 7 Aug 2015 09:26:53 -0700 Subject: [PATCH 4/4] Revert Cocoapod version update --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index cf8e89d790..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.38.2 +COCOAPODS: 0.37.2