diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 11dfe74e10..a1767edf63 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -213,6 +213,16 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { */ @property (nonatomic, assign) BOOL passthroughNonlinkTouches; +#pragma mark - ASTextKit Customization +/** + A block to provide a hook to provide a custom NSLayoutManager to the ASTextKitRenderer + */ +@property (nonatomic, copy) NSLayoutManager * (^layoutManagerCreationBlock)(void); + +/** + A block to provide a hook to provide a NSTextStorage to the Text Kit's layout manager. + */ +@property (nonatomic, copy) NSTextStorage * (^textStorageCreationBlock)(NSAttributedString *attributedString); @end diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index f78d929009..3cc4e7ccbd 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -245,6 +245,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; .exclusionPaths = _exclusionPaths, .pointSizeScaleFactors = _pointSizeScaleFactors, .currentScaleFactor = self.currentScaleFactor, + .layoutManagerCreationBlock = self.layoutManagerCreationBlock, + .textStorageCreationBlock = self.textStorageCreationBlock, }; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h index 4d2160ea33..fab9cdf7cc 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h +++ b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h @@ -91,15 +91,20 @@ struct ASTextKitAttributes { */ CGFloat currentScaleFactor; /** - A pointer to a function that that returns a custom layout manager subclass. If nil, defaults to NSLayoutManager. + An optional block that returns a custom layout manager subclass. If nil, defaults to NSLayoutManager. */ - NSLayoutManager *(*layoutManagerFactory)(void); + NSLayoutManager * (^layoutManagerCreationBlock)(void); /** An optional delegate for the NSLayoutManager */ id layoutManagerDelegate; + /** + An optional block that returns a custom NSTextStorage for the layout manager. + */ + NSTextStorage * (^textStorageCreationBlock)(NSAttributedString *attributedString); + /** We provide an explicit copy function so we can use aggregate initializer syntax while providing copy semantics for the NSObjects inside. @@ -119,8 +124,9 @@ struct ASTextKitAttributes { shadowRadius, pointSizeScaleFactors, currentScaleFactor, - layoutManagerFactory, + layoutManagerCreationBlock, layoutManagerDelegate, + textStorageCreationBlock, }; }; @@ -133,7 +139,8 @@ struct ASTextKitAttributes { && shadowRadius == other.shadowRadius && [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors] && currentScaleFactor == currentScaleFactor - && layoutManagerFactory == other.layoutManagerFactory + && layoutManagerCreationBlock == other.layoutManagerCreationBlock + && textStorageCreationBlock == other.textStorageCreationBlock && CGSizeEqualToSize(shadowOffset, other.shadowOffset) && _objectsEqual(exclusionPaths, other.exclusionPaths) && _objectsEqual(avoidTailTruncationSet, other.avoidTailTruncationSet) diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.mm b/AsyncDisplayKit/TextKit/ASTextKitAttributes.mm index 5ca015fd84..e4b16f25a0 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitAttributes.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitAttributes.mm @@ -23,7 +23,8 @@ size_t ASTextKitAttributes::hash() const [attributedString hash], [truncationAttributedString hash], [avoidTailTruncationSet hash], - std::hash()((NSUInteger) layoutManagerFactory), + std::hash()((NSUInteger) layoutManagerCreationBlock), + std::hash()((NSUInteger) textStorageCreationBlock), std::hash()(lineBreakMode), std::hash()(maximumNumberOfLines), [exclusionPaths hash], diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.h b/AsyncDisplayKit/TextKit/ASTextKitContext.h index 9fe6ab6af1..b9cd371c1e 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.h +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.h @@ -28,8 +28,9 @@ maximumNumberOfLines:(NSUInteger)maximumNumberOfLines exclusionPaths:(NSArray *)exclusionPaths constrainedSize:(CGSize)constrainedSize - layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory - layoutManagerDelegate:(id)layoutManagerDelegate; + layoutManagerCreationBlock:(NSLayoutManager * (^)(void))layoutCreationBlock + layoutManagerDelegate:(id)layoutManagerDelegate + textStorageCreationBlock:(NSTextStorage * (^)(NSAttributedString *attributedString))textStorageCreationBlock; @property (nonatomic, assign, readwrite) CGSize constrainedSize; diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.mm b/AsyncDisplayKit/TextKit/ASTextKitContext.mm index 42d41af217..b9925d4f7e 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.mm @@ -29,16 +29,22 @@ maximumNumberOfLines:(NSUInteger)maximumNumberOfLines exclusionPaths:(NSArray *)exclusionPaths constrainedSize:(CGSize)constrainedSize - layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory + layoutManagerCreationBlock:(NSLayoutManager * (^)(void))layoutCreationBlock layoutManagerDelegate:(id)layoutManagerDelegate + textStorageCreationBlock:(NSTextStorage * (^)(NSAttributedString *attributedString))textStorageCreationBlock + { if (self = [super init]) { // Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock. static std::mutex __static_mutex; std::lock_guard l(__static_mutex); // Create the TextKit component stack with our default configuration. - _textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]); - _layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[ASLayoutManager alloc] init]; + if (textStorageCreationBlock) { + _textStorage = textStorageCreationBlock(attributedString); + } else { + _textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]); + } + _layoutManager = layoutCreationBlock ? layoutCreationBlock() : [[ASLayoutManager alloc] init]; _layoutManager.usesFontLeading = NO; _layoutManager.delegate = layoutManagerDelegate; [_textStorage addLayoutManager:_layoutManager]; diff --git a/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm b/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm index f1be0df651..f881ae5f34 100644 --- a/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm @@ -81,8 +81,8 @@ static std::mutex __static_mutex; std::lock_guard l(__static_mutex); - NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; - NSLayoutManager *layoutManager = _attributes.layoutManagerFactory ? _attributes.layoutManagerFactory() : [[ASLayoutManager alloc] init]; + NSTextStorage *textStorage = _attributes.textStorageCreationBlock ? _attributes.textStorageCreationBlock(attributedString) : [[NSTextStorage alloc] initWithAttributedString:attributedString]; + NSLayoutManager *layoutManager = _attributes.layoutManagerCreationBlock ? _attributes.layoutManagerCreationBlock() : [[ASLayoutManager alloc] init]; layoutManager.usesFontLeading = NO; [textStorage addLayoutManager:layoutManager]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:_constrainedSize]; diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index 825ce2e26e..5ad0e302d3 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -104,8 +104,9 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() maximumNumberOfLines:attributes.maximumNumberOfLines exclusionPaths:attributes.exclusionPaths constrainedSize:shadowConstrainedSize - layoutManagerFactory:attributes.layoutManagerFactory - layoutManagerDelegate:attributes.layoutManagerDelegate]; + layoutManagerCreationBlock:attributes.layoutManagerCreationBlock + layoutManagerDelegate:attributes.layoutManagerDelegate + textStorageCreationBlock:attributes.textStorageCreationBlock]; } return _context; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm b/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm index 0c9870b3ce..1ea45e2b91 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm @@ -72,8 +72,9 @@ maximumNumberOfLines:1 exclusionPaths:nil constrainedSize:constrainedRect.size - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; __block CGRect truncationUsedRect; [truncationContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *truncationLayoutManager, NSTextStorage *truncationTextStorage, NSTextContainer *truncationTextContainer) { diff --git a/AsyncDisplayKitTests/ASTextKitTruncationTests.mm b/AsyncDisplayKitTests/ASTextKitTruncationTests.mm index 2bd0ba330a..d82ae0fd2d 100644 --- a/AsyncDisplayKitTests/ASTextKitTruncationTests.mm +++ b/AsyncDisplayKitTests/ASTextKitTruncationTests.mm @@ -42,8 +42,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; __block NSRange textKitVisibleRange; [context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { textKitVisibleRange = [layoutManager characterRangeForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] @@ -64,8 +65,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@""]]; @@ -87,8 +89,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; @@ -111,8 +114,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; @@ -136,8 +140,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil - layoutManagerDelegate:nil]; + layoutManagerCreationBlock:nil + layoutManagerDelegate:nil + textStorageCreationBlock:nil]; XCTAssertNoThrow([[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString]