Remove the workaround used for measuring multiline text on iOS (#50028)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50028

Changelog: [IOS][CHANGED] - Replace a workaround for measuring multiline text with `maximumNumberOfLines` on iOS with a proper solution

## Summary:

In https://github.com/facebook/react-native/pull/49549 support for `numberOfLines` was added to iOS `TextInput`. Along that, a workaround for measuring multiline text with limited number of lines was added to avoid an edge case coming from `NSTextContainer` measuring empty lines even if they are over the line limit.

This PR handles that case properly by counting and measuring individual lines to properly handle the offending edge case.

Reviewed By: NickGerleman

Differential Revision: D71111841

fbshipit-source-id: 6adb4450a13fcc845604622ea76576658a7537aa
This commit is contained in:
Jakub Piasecki
2025-03-17 00:14:29 -07:00
committed by Facebook GitHub Bot
parent fc7385c1ea
commit 77cdaa8733
@@ -47,24 +47,11 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
}
CGSize maximumSize = CGSize{layoutConstraints.maximumSize.width, CGFLOAT_MAX};
// A workaround for the issue with empty line measurement:
// When maximumNumberOfLines is set to N and N+1 line is empty, the returned
// measurement is for N+1 lines. Adding any character to that line results
// in the correct measurement.
if (paragraphAttributes.maximumNumberOfLines > 0 && attributedString.length > 0 &&
[[attributedString string] characterAtIndex:attributedString.length - 1] == '\n') {
NSMutableAttributedString *mutableString =
[[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
[mutableString replaceCharactersInRange:NSMakeRange(attributedString.length - 1, 1) withString:@"\n "];
attributedString = mutableString;
}
NSTextStorage *textStorage = [self _textStorageAndLayoutManagerWithAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:maximumSize];
return [self _measureTextStorage:textStorage];
return [self _measureTextStorage:textStorage paragraphAttributes:paragraphAttributes];
}
- (TextMeasurement)measureAttributedString:(AttributedString)attributedString
@@ -341,6 +328,7 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
}
- (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
{
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -348,6 +336,8 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
__block BOOL textDidWrap = NO;
__block NSUInteger linesEnumerated = 0;
__block CGFloat enumeratedLinesHeight = 0;
[layoutManager
enumerateLineFragmentsForGlyphRange:glyphRange
usingBlock:^(
@@ -363,6 +353,13 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
[textStorage.string characterAtIndex:lastCharacterIndex] == '\n';
if (!endsWithNewLine && textStorage.string.length > lastCharacterIndex + 1) {
textDidWrap = YES;
}
if (linesEnumerated++ < paragraphAttributes.maximumNumberOfLines) {
enumeratedLinesHeight = usedRect.origin.y + usedRect.size.height;
}
if (textDidWrap &&
(paragraphAttributes.maximumNumberOfLines == 0 ||
linesEnumerated >= paragraphAttributes.maximumNumberOfLines)) {
*stop = YES;
}
}];
@@ -373,6 +370,18 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
size.width = textContainer.size.width;
}
if (paragraphAttributes.maximumNumberOfLines != 0) {
// If maximumNumberOfLines is set, we cannot rely on setting it on the NSTextContainer
// due to an edge case where it returns wrong height:
// When maximumNumberOfLines is set to N and the N+1 line is empty, the measured height
// is N+1 lines (incorrect). Adding any characted to the N+1 line, making it non-empty
// casuses the measured height to be N lines (correct).
if (linesEnumerated < paragraphAttributes.maximumNumberOfLines) {
enumeratedLinesHeight += layoutManager.extraLineFragmentUsedRect.size.height;
}
size.height = enumeratedLinesHeight;
}
size = (CGSize){RCTCeilPixelValue(size.width), RCTCeilPixelValue(size.height)};
__block auto attachments = TextMeasurement::Attachments{};