3232
3333@implementation ASTextKitRenderer {
3434 CGSize _calculatedSize;
35+ BOOL _sizeIsCalculated;
3536}
37+ @synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater;
3638
3739#pragma mark - Initialization
3840
@@ -42,30 +44,50 @@ - (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attribute
4244 if (self = [super init ]) {
4345 _constrainedSize = constrainedSize;
4446 _attributes = attributes;
47+ _sizeIsCalculated = NO ;
48+ }
49+ return self;
50+ }
4551
52+ - (ASTextKitShadower *)shadower
53+ {
54+ if (!_shadower) {
55+ ASTextKitAttributes attributes = _attributes;
4656 _shadower = [[ASTextKitShadower alloc ] initWithShadowOffset: attributes.shadowOffset
4757 shadowColor: attributes.shadowColor
4858 shadowOpacity: attributes.shadowOpacity
4959 shadowRadius: attributes.shadowRadius];
60+ }
61+ return _shadower;
62+ }
5063
64+ - (ASTextKitTailTruncater *)truncater
65+ {
66+ if (!_truncater) {
67+ ASTextKitAttributes attributes = _attributes;
5168 // We must inset the constrained size by the size of the shadower.
52- CGSize shadowConstrainedSize = [_shadower insetSizeWithConstrainedSize: _constrainedSize];
69+ CGSize shadowConstrainedSize = [[self shadower ] insetSizeWithConstrainedSize: _constrainedSize];
70+ _truncater = [[ASTextKitTailTruncater alloc ] initWithContext: [self context ]
71+ truncationAttributedString: attributes.truncationAttributedString
72+ avoidTailTruncationSet: attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet ()
73+ constrainedSize: shadowConstrainedSize];
74+ }
75+ return _truncater;
76+ }
5377
78+ - (ASTextKitContext *)context
79+ {
80+ if (!_context) {
81+ ASTextKitAttributes attributes = _attributes;
82+ CGSize shadowConstrainedSize = [[self shadower ] insetSizeWithConstrainedSize: _constrainedSize];
5483 _context = [[ASTextKitContext alloc ] initWithAttributedString: attributes.attributedString
5584 lineBreakMode: attributes.lineBreakMode
5685 maximumNumberOfLines: attributes.maximumNumberOfLines
5786 exclusionPaths: attributes.exclusionPaths
5887 constrainedSize: shadowConstrainedSize
5988 layoutManagerFactory: attributes.layoutManagerFactory];
60-
61- _truncater = [[ASTextKitTailTruncater alloc ] initWithContext: _context
62- truncationAttributedString: attributes.truncationAttributedString
63- avoidTailTruncationSet: attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet ()
64- constrainedSize: shadowConstrainedSize];
65-
66- [self _calculateSize ];
6789 }
68- return self ;
90+ return _context ;
6991}
7092
7193#pragma mark - Sizing
@@ -74,14 +96,14 @@ - (void)_calculateSize
7496{
7597 // Force glyph generation and layout, which may not have happened yet (and isn't triggered by
7698 // -usedRectForTextContainer:).
77- [_context performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
99+ [[ self context ] performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
78100 [layoutManager ensureLayoutForTextContainer: textContainer];
79101 }];
80102
81103
82104 CGRect constrainedRect = {CGPointZero, _constrainedSize};
83105 __block CGRect boundingRect;
84- [_context performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
106+ [[ self context ] performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
85107 boundingRect = [layoutManager usedRectForTextContainer: textContainer];
86108 }];
87109
@@ -94,6 +116,10 @@ - (void)_calculateSize
94116
95117- (CGSize)size
96118{
119+ if (!_sizeIsCalculated) {
120+ [self _calculateSize ];
121+ _sizeIsCalculated = YES ;
122+ }
97123 return _calculatedSize;
98124}
99125
@@ -104,13 +130,13 @@ - (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds;
104130 // We add an assertion so we can track the rare conditions where a graphics context is not present
105131 ASDisplayNodeAssertNotNil (context, @" This is no good without a context." );
106132
107- CGRect shadowInsetBounds = [_shadower insetRectWithConstrainedRect: bounds];
133+ CGRect shadowInsetBounds = [[ self shadower ] insetRectWithConstrainedRect: bounds];
108134
109135 CGContextSaveGState (context);
110- [_shadower setShadowInContext: context];
136+ [[ self shadower ] setShadowInContext: context];
111137 UIGraphicsPushContext (context);
112138
113- [_context performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
139+ [[ self context ] performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
114140 NSRange glyphRange = [layoutManager glyphRangeForTextContainer: textContainer];
115141 [layoutManager drawBackgroundForGlyphRange: glyphRange atPoint: shadowInsetBounds.origin];
116142 [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: shadowInsetBounds.origin];
@@ -125,7 +151,7 @@ - (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds;
125151- (NSUInteger )lineCount
126152{
127153 __block NSUInteger lineCount = 0 ;
128- [_context performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
154+ [[ self context ] performBlockWithLockedTextKitComponents: ^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
129155 for (NSRange lineRange = { 0 , 0 }; NSMaxRange (lineRange) < [layoutManager numberOfGlyphs ]; lineCount++) {
130156 [layoutManager lineFragmentRectForGlyphAtIndex: NSMaxRange (lineRange) effectiveRange: &lineRange];
131157 }
@@ -135,7 +161,7 @@ - (NSUInteger)lineCount
135161
136162- (std::vector<NSRange>)visibleRanges
137163{
138- return _truncater .visibleRanges ;
164+ return [ self truncater ] .visibleRanges ;
139165}
140166
141167@end
0 commit comments