Add ability to customize NSLayoutManager and NSTextStorage when created in the ASTextKitContext

This commit is contained in:
rcancro 2016-02-22 13:00:02 -08:00
parent 896f0adcaf
commit 00b0968bf7
10 changed files with 60 additions and 26 deletions

View File

@ -213,6 +213,16 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
*/ */
@property (nonatomic, assign) BOOL passthroughNonlinkTouches; @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 @end

View File

@ -245,6 +245,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
.exclusionPaths = _exclusionPaths, .exclusionPaths = _exclusionPaths,
.pointSizeScaleFactors = _pointSizeScaleFactors, .pointSizeScaleFactors = _pointSizeScaleFactors,
.currentScaleFactor = self.currentScaleFactor, .currentScaleFactor = self.currentScaleFactor,
.layoutManagerCreationBlock = self.layoutManagerCreationBlock,
.textStorageCreationBlock = self.textStorageCreationBlock,
}; };
} }

View File

@ -91,15 +91,20 @@ struct ASTextKitAttributes {
*/ */
CGFloat currentScaleFactor; 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 An optional delegate for the NSLayoutManager
*/ */
id<NSLayoutManagerDelegate> layoutManagerDelegate; id<NSLayoutManagerDelegate> 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 We provide an explicit copy function so we can use aggregate initializer syntax while providing copy semantics for
the NSObjects inside. the NSObjects inside.
@ -119,8 +124,9 @@ struct ASTextKitAttributes {
shadowRadius, shadowRadius,
pointSizeScaleFactors, pointSizeScaleFactors,
currentScaleFactor, currentScaleFactor,
layoutManagerFactory, layoutManagerCreationBlock,
layoutManagerDelegate, layoutManagerDelegate,
textStorageCreationBlock,
}; };
}; };
@ -133,7 +139,8 @@ struct ASTextKitAttributes {
&& shadowRadius == other.shadowRadius && shadowRadius == other.shadowRadius
&& [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors] && [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors]
&& currentScaleFactor == currentScaleFactor && currentScaleFactor == currentScaleFactor
&& layoutManagerFactory == other.layoutManagerFactory && layoutManagerCreationBlock == other.layoutManagerCreationBlock
&& textStorageCreationBlock == other.textStorageCreationBlock
&& CGSizeEqualToSize(shadowOffset, other.shadowOffset) && CGSizeEqualToSize(shadowOffset, other.shadowOffset)
&& _objectsEqual(exclusionPaths, other.exclusionPaths) && _objectsEqual(exclusionPaths, other.exclusionPaths)
&& _objectsEqual(avoidTailTruncationSet, other.avoidTailTruncationSet) && _objectsEqual(avoidTailTruncationSet, other.avoidTailTruncationSet)

View File

@ -23,7 +23,8 @@ size_t ASTextKitAttributes::hash() const
[attributedString hash], [attributedString hash],
[truncationAttributedString hash], [truncationAttributedString hash],
[avoidTailTruncationSet hash], [avoidTailTruncationSet hash],
std::hash<NSUInteger>()((NSUInteger) layoutManagerFactory), std::hash<NSUInteger>()((NSUInteger) layoutManagerCreationBlock),
std::hash<NSUInteger>()((NSUInteger) textStorageCreationBlock),
std::hash<NSInteger>()(lineBreakMode), std::hash<NSInteger>()(lineBreakMode),
std::hash<NSInteger>()(maximumNumberOfLines), std::hash<NSInteger>()(maximumNumberOfLines),
[exclusionPaths hash], [exclusionPaths hash],

View File

@ -28,8 +28,9 @@
maximumNumberOfLines:(NSUInteger)maximumNumberOfLines maximumNumberOfLines:(NSUInteger)maximumNumberOfLines
exclusionPaths:(NSArray *)exclusionPaths exclusionPaths:(NSArray *)exclusionPaths
constrainedSize:(CGSize)constrainedSize constrainedSize:(CGSize)constrainedSize
layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory layoutManagerCreationBlock:(NSLayoutManager * (^)(void))layoutCreationBlock
layoutManagerDelegate:(id<NSLayoutManagerDelegate>)layoutManagerDelegate; layoutManagerDelegate:(id<NSLayoutManagerDelegate>)layoutManagerDelegate
textStorageCreationBlock:(NSTextStorage * (^)(NSAttributedString *attributedString))textStorageCreationBlock;
@property (nonatomic, assign, readwrite) CGSize constrainedSize; @property (nonatomic, assign, readwrite) CGSize constrainedSize;

View File

@ -29,16 +29,22 @@
maximumNumberOfLines:(NSUInteger)maximumNumberOfLines maximumNumberOfLines:(NSUInteger)maximumNumberOfLines
exclusionPaths:(NSArray *)exclusionPaths exclusionPaths:(NSArray *)exclusionPaths
constrainedSize:(CGSize)constrainedSize constrainedSize:(CGSize)constrainedSize
layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory layoutManagerCreationBlock:(NSLayoutManager * (^)(void))layoutCreationBlock
layoutManagerDelegate:(id<NSLayoutManagerDelegate>)layoutManagerDelegate layoutManagerDelegate:(id<NSLayoutManagerDelegate>)layoutManagerDelegate
textStorageCreationBlock:(NSTextStorage * (^)(NSAttributedString *attributedString))textStorageCreationBlock
{ {
if (self = [super init]) { if (self = [super init]) {
// Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock. // Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock.
static std::mutex __static_mutex; static std::mutex __static_mutex;
std::lock_guard<std::mutex> l(__static_mutex); std::lock_guard<std::mutex> l(__static_mutex);
// Create the TextKit component stack with our default configuration. // Create the TextKit component stack with our default configuration.
_textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]); if (textStorageCreationBlock) {
_layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[ASLayoutManager alloc] init]; _textStorage = textStorageCreationBlock(attributedString);
} else {
_textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]);
}
_layoutManager = layoutCreationBlock ? layoutCreationBlock() : [[ASLayoutManager alloc] init];
_layoutManager.usesFontLeading = NO; _layoutManager.usesFontLeading = NO;
_layoutManager.delegate = layoutManagerDelegate; _layoutManager.delegate = layoutManagerDelegate;
[_textStorage addLayoutManager:_layoutManager]; [_textStorage addLayoutManager:_layoutManager];

View File

@ -81,8 +81,8 @@
static std::mutex __static_mutex; static std::mutex __static_mutex;
std::lock_guard<std::mutex> l(__static_mutex); std::lock_guard<std::mutex> l(__static_mutex);
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; NSTextStorage *textStorage = _attributes.textStorageCreationBlock ? _attributes.textStorageCreationBlock(attributedString) : [[NSTextStorage alloc] initWithAttributedString:attributedString];
NSLayoutManager *layoutManager = _attributes.layoutManagerFactory ? _attributes.layoutManagerFactory() : [[ASLayoutManager alloc] init]; NSLayoutManager *layoutManager = _attributes.layoutManagerCreationBlock ? _attributes.layoutManagerCreationBlock() : [[ASLayoutManager alloc] init];
layoutManager.usesFontLeading = NO; layoutManager.usesFontLeading = NO;
[textStorage addLayoutManager:layoutManager]; [textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:_constrainedSize]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:_constrainedSize];

View File

@ -104,8 +104,9 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
maximumNumberOfLines:attributes.maximumNumberOfLines maximumNumberOfLines:attributes.maximumNumberOfLines
exclusionPaths:attributes.exclusionPaths exclusionPaths:attributes.exclusionPaths
constrainedSize:shadowConstrainedSize constrainedSize:shadowConstrainedSize
layoutManagerFactory:attributes.layoutManagerFactory layoutManagerCreationBlock:attributes.layoutManagerCreationBlock
layoutManagerDelegate:attributes.layoutManagerDelegate]; layoutManagerDelegate:attributes.layoutManagerDelegate
textStorageCreationBlock:attributes.textStorageCreationBlock];
} }
return _context; return _context;
} }

View File

@ -72,8 +72,9 @@
maximumNumberOfLines:1 maximumNumberOfLines:1
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedRect.size constrainedSize:constrainedRect.size
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
__block CGRect truncationUsedRect; __block CGRect truncationUsedRect;
[truncationContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *truncationLayoutManager, NSTextStorage *truncationTextStorage, NSTextContainer *truncationTextContainer) { [truncationContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *truncationLayoutManager, NSTextStorage *truncationTextStorage, NSTextContainer *truncationTextContainer) {

View File

@ -42,8 +42,9 @@
maximumNumberOfLines:0 maximumNumberOfLines:0
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
__block NSRange textKitVisibleRange; __block NSRange textKitVisibleRange;
[context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
textKitVisibleRange = [layoutManager characterRangeForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] textKitVisibleRange = [layoutManager characterRangeForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer]
@ -64,8 +65,9 @@
maximumNumberOfLines:0 maximumNumberOfLines:0
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
truncationAttributedString:[self _simpleTruncationAttributedString] truncationAttributedString:[self _simpleTruncationAttributedString]
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@""]]; avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@""]];
@ -87,8 +89,9 @@
maximumNumberOfLines:0 maximumNumberOfLines:0
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
truncationAttributedString:[self _simpleTruncationAttributedString] truncationAttributedString:[self _simpleTruncationAttributedString]
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]];
@ -111,8 +114,9 @@
maximumNumberOfLines:0 maximumNumberOfLines:0
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
truncationAttributedString:[self _simpleTruncationAttributedString] truncationAttributedString:[self _simpleTruncationAttributedString]
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]];
@ -136,8 +140,9 @@
maximumNumberOfLines:0 maximumNumberOfLines:0
exclusionPaths:nil exclusionPaths:nil
constrainedSize:constrainedSize constrainedSize:constrainedSize
layoutManagerFactory:nil layoutManagerCreationBlock:nil
layoutManagerDelegate:nil]; layoutManagerDelegate:nil
textStorageCreationBlock:nil];
XCTAssertNoThrow([[ASTextKitTailTruncater alloc] initWithContext:context XCTAssertNoThrow([[ASTextKitTailTruncater alloc] initWithContext:context
truncationAttributedString:[self _simpleTruncationAttributedString] truncationAttributedString:[self _simpleTruncationAttributedString]