Fixed some text shrinking issues

Previously I was multiplying the calculated height of the text by the scale factor. This fails in the case where we have a long string that doesn't fit, but it is shrunk so much that it now has more than enough room in the renderer's constrained size. In this case we fail to update the calculated size's width.

Also updated `lineCountForString` in `ASTextKitFontSizeAdjuster` to reuse a sizer layout manager and text container.
This commit is contained in:
rcancro
2016-03-09 15:27:33 -08:00
parent de4e4db671
commit bcb1832fcd
2 changed files with 58 additions and 32 deletions

View File

@@ -22,6 +22,8 @@
std::mutex _textKitMutex;
BOOL _measured;
CGFloat _scaleFactor;
NSLayoutManager *_sizingLayoutManager;
NSTextContainer *_sizingTextContainer;
}
- (instancetype)initWithContext:(ASTextKitContext *)context
@@ -78,30 +80,38 @@
- (NSUInteger)lineCountForString:(NSAttributedString *)attributedString
{
NSUInteger lineCount = 0;
NSUInteger lineCount = 0;
static std::mutex __static_mutex;
std::lock_guard<std::mutex> l(__static_mutex);
static std::mutex __static_mutex;
std::lock_guard<std::mutex> l(__static_mutex);
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:CGSizeMake(_constrainedSize.width, FLT_MAX)];
NSTextStorage *textStorage = _attributes.textStorageCreationBlock ? _attributes.textStorageCreationBlock(attributedString) : [[NSTextStorage alloc] initWithAttributedString:attributedString];
if (_sizingLayoutManager == nil) {
_sizingLayoutManager = _attributes.layoutManagerCreationBlock ? _attributes.layoutManagerCreationBlock() : [[ASLayoutManager alloc] init];
_sizingLayoutManager.usesFontLeading = NO;
}
[textStorage addLayoutManager:_sizingLayoutManager];
if (_sizingTextContainer == nil) {
// make this text container unbounded in height so that the layout manager will compute the total
// number of lines and not stop counting when height runs out.
_sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, FLT_MAX)];
_sizingTextContainer.lineFragmentPadding = 0;
textContainer.lineFragmentPadding = 0;
textContainer.lineBreakMode = _attributes.lineBreakMode;
// use 0 regardless of what is in the attributes so that we get an accurate line count
_sizingTextContainer.maximumNumberOfLines = 0;
[_sizingLayoutManager addTextContainer:_sizingTextContainer];
}
// use 0 regardless of what is in the attributes so that we get an accurate line count
textContainer.maximumNumberOfLines = 0;
textContainer.exclusionPaths = _attributes.exclusionPaths;
[layoutManager addTextContainer:textContainer];
_sizingTextContainer.lineBreakMode = _attributes.lineBreakMode;
_sizingTextContainer.exclusionPaths = _attributes.exclusionPaths;
for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs] && lineCount <= _attributes.maximumNumberOfLines; lineCount++) {
[layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange];
}
return lineCount;
for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [_sizingLayoutManager numberOfGlyphs] && lineCount <= _attributes.maximumNumberOfLines; lineCount++) {
[_sizingLayoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange];
}
[textStorage removeLayoutManager:_sizingLayoutManager];
return lineCount;
}
- (CGFloat)scaleFactor

View File

@@ -145,7 +145,17 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
// -usedRectForTextContainer:).
__block NSTextStorage *scaledTextStorage = nil;
BOOL isScaled = [self isScaled];
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
if (isScaled) {
NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage];
[ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:_currentScaleFactor];
scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString];
[textStorage removeLayoutManager:layoutManager];
[scaledTextStorage addLayoutManager:layoutManager];
}
[layoutManager ensureLayoutForTextContainer:textContainer];
}];
@@ -153,6 +163,11 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
__block CGRect boundingRect;
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
boundingRect = [layoutManager usedRectForTextContainer:textContainer];
if (isScaled) {
// put the non-scaled version back
[scaledTextStorage removeLayoutManager:layoutManager];
[textStorage addLayoutManager:layoutManager];
}
}];
// TextKit often returns incorrect glyph bounding rects in the horizontal direction, so we clip to our bounding rect
@@ -160,10 +175,11 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
boundingRect = CGRectIntersection(boundingRect, {.size = constrainedRect.size});
CGSize boundingSize = [_shadower outsetSizeWithInsetSize:boundingRect.size];
_calculatedSize = CGSizeMake(boundingSize.width, boundingSize.height);
}
if (_currentScaleFactor > 0.0 && _currentScaleFactor < 1.0) {
_calculatedSize.height = ceilf(_calculatedSize.height * _currentScaleFactor);
}
- (BOOL)isScaled
{
return (self.currentScaleFactor > 0 && self.currentScaleFactor < 1.0);
}
#pragma mark - Drawing
@@ -189,7 +205,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
NSTextStorage *scaledTextStorage = nil;
BOOL isScaled = (self.currentScaleFactor > 0 && self.currentScaleFactor < 1.0);
BOOL isScaled = [self isScaled];
if (isScaled) {
// if we are going to scale the text, swap out the non-scaled text for the scaled version.