|
|
|
|
@@ -45,85 +45,6 @@ static const CGFloat ASTextNodeHighlightLightOpacity = 0.11;
|
|
|
|
|
static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22;
|
|
|
|
|
static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute";
|
|
|
|
|
|
|
|
|
|
#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 == object.attributes
|
|
|
|
|
&& 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.totalCostLimit = 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 stack-allocated keys.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Disabled renderer cache for now
|
|
|
|
|
//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 cost:1];
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return renderer;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, CGSize constrainedSize)
|
|
|
|
|
{
|
|
|
|
|
return [[ASTextKitRenderer alloc] initWithTextKitAttributes:attributes constrainedSize:constrainedSize];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - ASTextNode
|
|
|
|
|
|
|
|
|
|
struct ASTextNodeDrawParameter {
|
|
|
|
|
CGRect bounds;
|
|
|
|
|
UIColor *backgroundColor;
|
|
|
|
|
@@ -154,6 +75,8 @@ struct ASTextNodeDrawParameter {
|
|
|
|
|
|
|
|
|
|
CGSize _constrainedSize;
|
|
|
|
|
|
|
|
|
|
ASTextKitRenderer *_renderer;
|
|
|
|
|
|
|
|
|
|
ASTextNodeDrawParameter _drawParameter;
|
|
|
|
|
|
|
|
|
|
UILongPressGestureRecognizer *_longPressGestureRecognizer;
|
|
|
|
|
@@ -220,6 +143,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
if (_shadowColor != NULL) {
|
|
|
|
|
CGColorRelease(_shadowColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
|
|
|
|
|
if (_longPressGestureRecognizer) {
|
|
|
|
|
_longPressGestureRecognizer.delegate = nil;
|
|
|
|
|
@@ -261,6 +186,29 @@ 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
|
|
|
|
|
{
|
|
|
|
|
[super didLoad];
|
|
|
|
|
@@ -275,6 +223,18 @@ 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
|
|
|
|
|
@@ -286,16 +246,20 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
{
|
|
|
|
|
ASDN::MutexLocker l(__instanceLock__);
|
|
|
|
|
|
|
|
|
|
CGSize constrainedSize;
|
|
|
|
|
if (_constrainedSize.width != -INFINITY) {
|
|
|
|
|
constrainedSize = _constrainedSize;
|
|
|
|
|
} else {
|
|
|
|
|
constrainedSize = bounds.size;
|
|
|
|
|
constrainedSize.width -= (_textContainerInset.left + _textContainerInset.right);
|
|
|
|
|
constrainedSize.height -= (_textContainerInset.top + _textContainerInset.bottom);
|
|
|
|
|
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 rendererForAttributes([self _rendererAttributes], constrainedSize);
|
|
|
|
|
return _renderer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (ASTextKitAttributes)_rendererAttributes
|
|
|
|
|
@@ -308,15 +272,51 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
.lineBreakMode = _truncationMode,
|
|
|
|
|
.maximumNumberOfLines = _maximumNumberOfLines,
|
|
|
|
|
.exclusionPaths = _exclusionPaths,
|
|
|
|
|
// use the property getter so a subclass can provide these scale factors on demand if desired
|
|
|
|
|
.pointSizeScaleFactors = self.pointSizeScaleFactors,
|
|
|
|
|
.shadowOffset = _shadowOffset,
|
|
|
|
|
.shadowColor = _cachedShadowUIColor,
|
|
|
|
|
.shadowOpacity = _shadowOpacity,
|
|
|
|
|
.shadowRadius = _shadowRadius,
|
|
|
|
|
// use the property getter so a subclass can provide these scale factors on demand if desired
|
|
|
|
|
.pointSizeScaleFactors = self.pointSizeScaleFactors
|
|
|
|
|
.shadowRadius = _shadowRadius
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (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.
|
|
|
|
|
__block ASTextKitRenderer *renderer = _renderer;
|
|
|
|
|
|
|
|
|
|
ASPerformBlockOnDeallocationQueue(^{
|
|
|
|
|
renderer = nil;
|
|
|
|
|
});
|
|
|
|
|
_renderer = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Layout and Sizing
|
|
|
|
|
|
|
|
|
|
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
|
|
|
|
|
@@ -337,6 +337,44 @@ 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)) {
|
|
|
|
|
if (boundsSize.width != rendererConstrainedSize.width) {
|
|
|
|
|
// Don't bother changing _constrainedSize, as ASDisplayNode's -measure: method would have a cache miss
|
|
|
|
|
// and ask us to recalculate layout if it were called with the same calculatedSize that got us to this point!
|
|
|
|
|
_renderer.constrainedSize = boundsSize;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
} else {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)calculatedLayoutDidChange
|
|
|
|
|
{
|
|
|
|
|
[super calculatedLayoutDidChange];
|
|
|
|
|
@@ -353,6 +391,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
|
|
|
|
|
if (CGSizeEqualToSize(_constrainedSize, layoutSize) == NO) {
|
|
|
|
|
_constrainedSize = layoutSize;
|
|
|
|
|
_renderer.constrainedSize = layoutSize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -373,20 +412,24 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
|
|
|
|
|
_constrainedSize = constrainedSize;
|
|
|
|
|
|
|
|
|
|
ASTextKitRenderer *renderer = [self _renderer];
|
|
|
|
|
// Instead of invalidating the renderer, in case this is a new call with a different constrained size,
|
|
|
|
|
// just update the size of the NSTextContainer that is owned by the renderer's internal context object.
|
|
|
|
|
[self _renderer].constrainedSize = _constrainedSize;
|
|
|
|
|
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
|
|
|
|
|
CGSize size = renderer.size;
|
|
|
|
|
CGSize size = [self _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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add textContainerInset to size
|
|
|
|
|
// Add the constrained size back textContainerInset
|
|
|
|
|
size.width += (_textContainerInset.left + _textContainerInset.right);
|
|
|
|
|
size.height += (_textContainerInset.top + _textContainerInset.bottom);
|
|
|
|
|
|
|
|
|
|
@@ -413,6 +456,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
|
|
|
|
|
- (void)setAttributedText:(NSAttributedString *)attributedText
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (attributedText == nil) {
|
|
|
|
|
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
|
|
|
|
|
}
|
|
|
|
|
@@ -432,6 +476,9 @@ 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;
|
|
|
|
|
@@ -446,6 +493,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
// Force display to create renderer with new size and redisplay with new string
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Accessiblity
|
|
|
|
|
self.accessibilityLabel = attributedText.string;
|
|
|
|
|
self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string.
|
|
|
|
|
@@ -462,6 +510,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_exclusionPaths = [exclusionPaths copy];
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self invalidateCalculatedLayout];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
@@ -764,11 +813,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|
|
|
|
if (highlightTargetLayer != nil) {
|
|
|
|
|
ASDN::MutexLocker l(__instanceLock__);
|
|
|
|
|
|
|
|
|
|
ASTextKitRenderer *renderer = [self _renderer];
|
|
|
|
|
NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock];
|
|
|
|
|
NSArray *highlightRects = [[self _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`,
|
|
|
|
|
@@ -1098,6 +1146,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|
|
|
|
}
|
|
|
|
|
_shadowColor = shadowColor;
|
|
|
|
|
_cachedShadowUIColor = [UIColor colorWithCGColor:shadowColor];
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1115,6 +1164,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|
|
|
|
|
|
|
|
|
if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) {
|
|
|
|
|
_shadowOffset = shadowOffset;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1132,6 +1182,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|
|
|
|
|
|
|
|
|
if (_shadowOpacity != shadowOpacity) {
|
|
|
|
|
_shadowOpacity = shadowOpacity;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1149,6 +1200,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|
|
|
|
|
|
|
|
|
if (_shadowRadius != shadowRadius) {
|
|
|
|
|
_shadowRadius = shadowRadius;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1207,6 +1259,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|
|
|
|
|
|
|
|
|
if (_truncationMode != truncationMode) {
|
|
|
|
|
_truncationMode = truncationMode;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1225,6 +1278,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|
|
|
|
|
|
|
|
|
if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) {
|
|
|
|
|
_pointSizeScaleFactors = pointSizeScaleFactors;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
@@ -1234,6 +1288,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|
|
|
|
|
|
|
|
|
if (_maximumNumberOfLines != maximumNumberOfLines) {
|
|
|
|
|
_maximumNumberOfLines = maximumNumberOfLines;
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1257,6 +1312,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|
|
|
|
- (void)_invalidateTruncationText
|
|
|
|
|
{
|
|
|
|
|
[self _updateComposedTruncationText];
|
|
|
|
|
[self _invalidateRenderer];
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|