Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,14 @@
minimumFontSize:(CGFloat)minimumFontSize
maximumFontSize:(CGFloat)maximumFontSize;

/**
* Like `-scaleFontSizeToFitSize:minimumFontSize:maximumFontSize:`, for text storages that are not laid out by an
* attached `NSLayoutManager`. `measureBlock` lays out the text storage as it currently is and returns the size of the
* laid out text, setting `isTruncated` if the last line is truncated.
*/
- (void)scaleFontSizeToFitSize:(CGSize)size
minimumFontSize:(CGFloat)minimumFontSize
maximumFontSize:(CGFloat)maximumFontSize
measureUsingBlock:(NS_NOESCAPE CGSize (^)(BOOL *isTruncated))measureBlock;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,32 @@ @implementation NSTextStorage (FontScaling)
- (void)scaleFontSizeToFitSize:(CGSize)size
minimumFontSize:(CGFloat)minimumFontSize
maximumFontSize:(CGFloat)maximumFontSize
{
[self scaleFontSizeToFitSize:size
minimumFontSize:minimumFontSize
maximumFontSize:maximumFontSize
measureUsingBlock:^CGSize(BOOL *isTruncated) {
NSLayoutManager *layoutManager = self.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;

[layoutManager ensureLayoutForTextContainer:textContainer];

NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
NSRange truncatedGlyphRange =
[layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRange.length - 1];
*isTruncated = truncatedGlyphRange.location != NSNotFound;

return [layoutManager usedRectForTextContainer:textContainer].size;
}];
}

- (void)scaleFontSizeToFitSize:(CGSize)size
minimumFontSize:(CGFloat)minimumFontSize
maximumFontSize:(CGFloat)maximumFontSize
measureUsingBlock:(NS_NOESCAPE CGSize (^)(BOOL *isTruncated))measureBlock
{
// Don't scale the font if it already fits
if ([self compareToSize:size thresholdRatio:0.01] & RCTTextSizeComparisonSmaller) {
if ([self compareToSize:size thresholdRatio:0.01 measureUsingBlock:measureBlock] & RCTTextSizeComparisonSmaller) {
return;
}

Expand All @@ -35,7 +58,9 @@ - (void)scaleFontSizeToFitSize:(CGSize)size
while (true) {
[self scaleFontSizeWithRatio:ratio minimumFontSize:minimumFontSize maximumFontSize:maximumFontSize];

RCTTextSizeComparisonOptions comparison = [self compareToSize:size thresholdRatio:0.01];
RCTTextSizeComparisonOptions comparison = [self compareToSize:size
thresholdRatio:0.01
measureUsingBlock:measureBlock];

if ((comparison & RCTTextSizeComparisonWithinRange) && (comparison & RCTTextSizeComparisonSmaller)) {
return;
Expand All @@ -61,11 +86,10 @@ - (void)scaleFontSizeToFitSize:(CGSize)size
}
}

- (RCTTextSizeComparisonOptions)compareToSize:(CGSize)size thresholdRatio:(CGFloat)thresholdRatio
- (RCTTextSizeComparisonOptions)compareToSize:(CGSize)size
thresholdRatio:(CGFloat)thresholdRatio
measureUsingBlock:(NS_NOESCAPE CGSize (^)(BOOL *isTruncated))measureBlock
{
NSLayoutManager *layoutManager = self.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;

// A workaround for truncatedGlyphRangeInLineFragmentForGlyphAtIndex returning NSNotFound when text has only
// one character and it gets truncated
if ([self length] == 1) {
Expand All @@ -75,18 +99,14 @@ - (RCTTextSizeComparisonOptions)compareToSize:(CGSize)size thresholdRatio:(CGFlo
}
}

[layoutManager ensureLayoutForTextContainer:textContainer];

// Does it fit the text container?
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
NSRange truncatedGlyphRange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRange.length - 1];
BOOL isTruncated = NO;
CGSize measuredSize = measureBlock(&isTruncated);

if (truncatedGlyphRange.location != NSNotFound) {
if (isTruncated) {
return RCTTextSizeComparisonLarger;
}

CGSize measuredSize = [layoutManager usedRectForTextContainer:textContainer].size;

// Does it fit the size?
BOOL fitsSize = size.width >= measuredSize.width && size.height >= measuredSize.height;

Expand Down
Loading
Loading