Skip to content

fix: Improve text line height calculation to properly align text on iOS#46884

Open
ArekChr wants to merge 61 commits into
facebook:mainfrom
ArekChr:improve-lineheight-calc-ios
Open

fix: Improve text line height calculation to properly align text on iOS#46884
ArekChr wants to merge 61 commits into
facebook:mainfrom
ArekChr:improve-lineheight-calc-ios

Conversation

@ArekChr
Copy link
Copy Markdown

@ArekChr ArekChr commented Oct 8, 2024

Summary:

This pull request addresses an issue in React Native on iOS: Text gets truncated when the line height is too small, even if the line-height matches or exceeds the font size.

The existing logic did not correctly handle font metrics (ascent, descent, top, bottom) when the total height exceeded the line height. The implementation now ensures a more balanced vertical space distribution, reducing text clipping and misalignment.

The fix mirrors a similar solution made on Android, ensuring consistency across both platforms.

For more context, see the related issue: React Native issue #29507.

Changelog:

[IOS] [FIXED] - Fixed an issue where text clipping and misalignment occurred when the line-height was smaller than the total ascent and descent of the font.

Test Plan:

The result is an improvement over the current implementation, reducing the occurrence of text truncation. While minimal truncation still exists at single-line heights, particularly with special characters, the implementation better preserves the vertical alignment of the text compared to previous behavior. However, like the Android implementation, this does not fully resolve the issue of React Native truncating content that is out of bounds of the span.

New Arch Before New Arch After
New Arch Before New Arch After

@facebook-github-bot
Copy link
Copy Markdown
Contributor

Hi @ArekChr!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@ArekChr ArekChr changed the title fix: Improve text line height calculation to properly align text fix: Improve text line height calculation to properly align text on iOS Oct 8, 2024
@facebook-github-bot
Copy link
Copy Markdown
Contributor

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Oct 8, 2024
contentFrame:(CGRect)contentFrame {
UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL];
if (!font) {
font = [UIFont systemFontOfSize:14];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn’t find systemFontSize used anywhere else in the repo, and the default font size of 14 has been hardcoded. Also, since systemFontSize is not available on tvOS, I’m not sure if it would be a suitable replacement. Let me know your thoughts on whether we should stick with the hardcoded value for consistency or if there’s a preferred approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NickGerleman

This seems sketch. E.g. what if there is a font multiplier applied?

Comment on lines +120 to +127
CGFloat verticalOffset = 0;
if (textHeight > lineHeight) {
CGFloat difference = textHeight - lineHeight;
verticalOffset = difference / 2.0;
} else if (textHeight < lineHeight) {
CGFloat difference = lineHeight - textHeight;
verticalOffset = -(difference / 2.0);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think comment explaining the calculations might be useful

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, added comments

@mrousavy
Copy link
Copy Markdown
Contributor

mrousavy commented Oct 8, 2024

nice!

@ArekChr ArekChr force-pushed the improve-lineheight-calc-ios branch from 246c550 to f998a9a Compare October 9, 2024 07:48
Copy link
Copy Markdown
Contributor

@cipolleschi cipolleschi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes looks good. We discussed this internally and we can proceed importing it.

To set the expectation, this might take some time to merge, given that this might affect internal tests and product code and we would need some time to fix them. But thankfully, we have a feature flag to control the rollout! 😄

@facebook-github-bot
Copy link
Copy Markdown
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@cipolleschi
Copy link
Copy Markdown
Contributor

@ArekChr CI is currently red. The error is

[React-RCTText] Compiling RCTTextView.mm
Error: use of undeclared identifier 'ReactNativeFeatureFlags'; did you mean 'facebook::react::ReactNativeFeatureFlags'?
  if (ReactNativeFeatureFlags::enableLineHeightCentering()) {

Could you please verify why this is happening and fix it?
You should be able to build it locally running these commands in the react-native root folder:

yarn install
cd packages/rn-tester
bundle install
bundle exec pod install
open RNTesterPods.xcworkspace

and then build from Xcode.

If you need to run the old architecture, you can disable it by adding:

- (BOOL) newArchEnabled
{
  return NO;
}

in the AppDelegate.mm file

@ArekChr
Copy link
Copy Markdown
Author

ArekChr commented Oct 9, 2024

Hi @cipolleschi, the issue has been fixed, and the build runs successfully locally. Thanks!

@cipolleschi
Copy link
Copy Markdown
Contributor

We were discussing internally that it would be better to have two separate feature flags, one for Android and one for iOS. Could I ask you to:

  1. revert the changes you made on the preexisting feature flag, which was targeting only Android
  2. add the iOS dual of that feature flag

The idea is to be able to control the two platforms separatedly.

Thank you so much for your help and patience

@ArekChr
Copy link
Copy Markdown
Author

ArekChr commented Oct 11, 2024

@cipolleschi I have an issue where the ReactNativeFeatureFlags.h header is not found when using dynamic frameworks. I’ve tried a few things but haven’t resolved the issue. Do you have any suggestions on how to fix or work around this?

@ArekChr ArekChr force-pushed the improve-lineheight-calc-ios branch from fa44b24 to 9e5ed70 Compare October 14, 2024 07:01
@ArekChr
Copy link
Copy Markdown
Author

ArekChr commented Oct 22, 2024

Hi, @cipolleschi. The CI failed due to some issues unrelated to my changes. I’ve likely fixed the problem with dynamic frameworks, but two workflows seem missing. Could you please trigger those workflows again so we can proceed?

@cipolleschi
Copy link
Copy Markdown
Contributor

@ArekChr I opened a ticket internally to find a better person to handle this, as it is out of my area of expertise.
The general sentiment is that it is something we do want, but the problem is that we don't have free capacity to work on this.

Even if we land the change as it is, behind feature flags so with no effect on the current behavior, we still need a Meta engineer that can run experiment and, eventually, enable the feature flag for everyone.

These feature flags are not something that our final users are supposed to change: so even by landing this, user would not probably benefit from it until we enable it for everyone.

I'll keep poking at people, asking to prioritize this work, but it might take a bit. :(

@ArekChr ArekChr force-pushed the improve-lineheight-calc-ios branch 2 times, most recently from 219dc0d to ffaa3ae Compare April 14, 2025 12:50
@ArekChr ArekChr requested a review from cipolleschi May 27, 2025 07:57
@ArekChr ArekChr force-pushed the improve-lineheight-calc-ios branch from babd31d to a17189e Compare June 9, 2025 10:02
Co-authored-by: troZee <12766071+troZee@users.noreply.github.com>
@react-native-bot
Copy link
Copy Markdown
Collaborator


Warnings
⚠️ ❗ JavaScript API change detected - This PR commits an update to ReactNativeApi.d.ts, indicating a change to React Native's public JavaScript API. Please include a clear changelog message. This change will be subject to extra review.

This change was flagged as: BREAKING

Generated by 🚫 dangerJS against 54bd5d7

@thymikee
Copy link
Copy Markdown
Contributor

@cipolleschi any chance we can push this forward? It’s a patch we need to apply in a few projects

- Rename calculateCenteredFrameWithAttributedText to adjustFrameForLineHeightCentering
- Change method to accept CGRect pointer instead of passing by value
- Properly modify frame using pointer dereferencing (frame->origin.y)
Changed from ceil/floor to round with remainder calculation to
prevent 1-pixel vertical shifts and ensure cross-platform parity.
@ArekChr
Copy link
Copy Markdown
Author

ArekChr commented Dec 12, 2025

Rebased with code style improvements. This PR fixes the long-standing iOS/Android line height centering discrepancy. Screenshots showing platform parity attached.

Before After
before-repro after-repro
before-example after-example

@matt-oakes
Copy link
Copy Markdown
Contributor

This is looking great @ArekChr!

To be clear, would this also resolve the issues in #49886 where text descenders get clipped on Android when the lineHeight is equal to the fontSize?

@DimitarNestorov
Copy link
Copy Markdown
Contributor

would this also resolve the issues ... on Android

@matt-oakes as per the PR title this fix is for iOS only

@a-klotz-p8
Copy link
Copy Markdown
Contributor

a-klotz-p8 commented Feb 4, 2026

Any updates on this? Would be great if this got merged!

Here is a react-native+0.83.1.patch file if anyone needs it that directly applies these changes without the feature flag.
Currently using this in my RN 0.83.1 app and it seems to work great.

The only concern I have with this is that this new centering could lead to similar issues of text being cut off with lineHeight being the same as the font size. Since that is something I have encountered on android. Is there a way to allow the characters drawing to overflow? This would solve some potential issues for both android and iOS imo.

@DimitarNestorov
Copy link
Copy Markdown
Contributor

I opened a ticket internally to find a better person to handle this, as it is out of my area of expertise. The general sentiment is that it is something we do want, but the problem is that we don't have free capacity to work on this.

@cipolleschi can you ask for eyes on that ticket. If there's feedback in the next few weeks we will find some bandwidth to address it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.