Reland: [iOS] Fixes ellipsis carries background from trimmed text (#45412)

Summary:
Reland https://github.com/facebook/react-native/issues/39408 . Try to fix the crash https://github.com/facebook/react-native/issues/37926#issuecomment-2225113557. cc. javache I changed the range from glyph to character because all attributes related APIs are character range.

## Changelog:

[IOS] [FIXED] - Fixes ellipsis carries background from trimmed text

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

Test Plan: https://github.com/facebook/react-native/issues/37926 .

Reviewed By: cipolleschi

Differential Revision: D59681679

Pulled By: javache

fbshipit-source-id: de4cb73e0304b8c0b0a40f4f6838b2c679747009
This commit is contained in:
zhongwuzw
2024-07-15 10:53:35 -07:00
committed by Facebook GitHub Bot
parent 24997dc5ae
commit 9e2f8859c4
2 changed files with 92 additions and 0 deletions
@@ -237,6 +237,8 @@
maximumFontSize:self.textAttributes.effectiveFont.pointSize];
}
[self processTruncatedAttributedText:textStorage textContainer:textContainer layoutManager:layoutManager];
if (!exclusiveOwnership) {
[_cachedTextStorages setObject:textStorage forKey:key];
}
@@ -244,6 +246,50 @@
return textStorage;
}
- (void)processTruncatedAttributedText:(NSTextStorage *)textStorage
textContainer:(NSTextContainer *)textContainer
layoutManager:(NSLayoutManager *)layoutManager
{
if (_maximumNumberOfLines > 0) {
[layoutManager ensureLayoutForTextContainer:textContainer];
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
__block int line = 0;
[layoutManager
enumerateLineFragmentsForGlyphRange:glyphRange
usingBlock:^(
CGRect rect,
CGRect usedRect,
NSTextContainer *_Nonnull _,
NSRange lineGlyphRange,
BOOL *_Nonnull stop) {
if (line == textContainer.maximumNumberOfLines - 1) {
NSRange truncatedRange = [layoutManager
truncatedGlyphRangeInLineFragmentForGlyphAtIndex:lineGlyphRange.location];
if (truncatedRange.location != NSNotFound) {
NSRange characterRange =
[layoutManager characterRangeForGlyphRange:truncatedRange
actualGlyphRange:nil];
if (characterRange.location > 0 && characterRange.length > 0) {
// Remove color attributes for truncated range
for (NSAttributedStringKey key in
@[ NSForegroundColorAttributeName, NSBackgroundColorAttributeName ]) {
[textStorage removeAttribute:key range:characterRange];
id attribute = [textStorage attribute:key
atIndex:characterRange.location - 1
effectiveRange:nil];
if (attribute) {
[textStorage addAttribute:key value:attribute range:characterRange];
}
}
}
}
}
line++;
}];
}
}
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext
{
// If the view got new `contentFrame`, we have to redraw it because
@@ -83,6 +83,9 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
#endif
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
[self processTruncatedAttributedText:textStorage textContainer:textContainer layoutManager:layoutManager];
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:frame.origin];
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:frame.origin];
@@ -123,6 +126,49 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
}
}
- (void)processTruncatedAttributedText:(NSTextStorage *)textStorage
textContainer:(NSTextContainer *)textContainer
layoutManager:(NSLayoutManager *)layoutManager
{
if (textContainer.maximumNumberOfLines > 0) {
[layoutManager ensureLayoutForTextContainer:textContainer];
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
__block int line = 0;
[layoutManager
enumerateLineFragmentsForGlyphRange:glyphRange
usingBlock:^(
CGRect rect,
CGRect usedRect,
NSTextContainer *_Nonnull _,
NSRange lineGlyphRange,
BOOL *_Nonnull stop) {
if (line == textContainer.maximumNumberOfLines - 1) {
NSRange truncatedRange = [layoutManager
truncatedGlyphRangeInLineFragmentForGlyphAtIndex:lineGlyphRange.location];
if (truncatedRange.location != NSNotFound) {
NSRange characterRange =
[layoutManager characterRangeForGlyphRange:truncatedRange
actualGlyphRange:nil];
if (characterRange.location > 0 && characterRange.length > 0) {
// Remove color attributes for truncated range
for (NSAttributedStringKey key in
@[ NSForegroundColorAttributeName, NSBackgroundColorAttributeName ]) {
[textStorage removeAttribute:key range:characterRange];
id attribute = [textStorage attribute:key
atIndex:characterRange.location - 1
effectiveRange:nil];
if (attribute) {
[textStorage addAttribute:key value:attribute range:characterRange];
}
}
}
}
}
line++;
}];
}
}
- (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
size:(CGSize)size