Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Baseline alignment does not work as expected with some font on iOS #27137

Closed
pahmed opened this issue Nov 6, 2019 · 1 comment
Closed

Baseline alignment does not work as expected with some font on iOS #27137

pahmed opened this issue Nov 6, 2019 · 1 comment
Labels
Bug Platform: iOS iOS applications. Resolution: Locked This issue was locked by the bot.

Comments

@pahmed
Copy link

pahmed commented Nov 6, 2019

React Native version:

System:
OS: macOS Mojave 10.14.6
CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
Memory: 3.94 GB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 10.15.0 - ~/.nvm/versions/node/v10.15.0/bin/node
Yarn: 1.17.3 - /usr/local/bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v10.15.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
Android SDK:
API Levels: 28, 29
Build Tools: 28.0.3, 29.0.0
IDEs:
Android Studio: 3.5 AI-191.8026.42.35.5900203
Xcode: 11.2/11B52 - /usr/bin/xcodebuild
npmPackages:
react: 16.9.0 => 16.9.0
react-native: 0.61.3 => 0.61.3
npmGlobalPackages:
react-native-cli: 2.0.1
react-native: 0.59.9

I'm getting a wrong behavior when using baseline alignment for text of specific font GothamNarrow-Medium, trying the same font using UILabel on iOS, I was getting correct results

Investigating this issue, I found that RCTTextView allocates a height more than needed, e.g.if I set the font size to 50, RCTextView allocates a height of 60, which adds a margin from the bottom, using UILabel on the other hand was allocating only 50 points height

This is the code for this

const App: () => React$Node = () => {
  return (
    <View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'baseline'}}>
      <View style={{width: 30, height: 30, backgroundColor: 'lightgray'}} />
      <Text style={{fontSize: 50, backgroundColor: 'yellow', fontFamily: 'GothamNarrow-Medium'}}>{'Settings'}</Text>
    </View>
  );
};

Screenshot 2019-11-06 at 11 10 17

As you can see, the text is not baseline aligned with the gray view

comparing this to using system font, you get the below result
Screenshot 2019-11-06 at 13 15 26

Trying the same thing with the custom font on iOS using UILabel, the behavior was as expected
Screenshot 2019-11-06 at 11 14 18

You can see here that the text is baseline aligned correctly with the red view, also there is no bottom margin and the height of the label is only 50

Some findings

While checking how the height is calculated, I found that this get calculated in RCTTextShadowView function RCTTextShadowViewMeasure

The native API layoutManager usedRectForTextContainer returns 60 for the height while font.lineHeight returns 50, which is not the case when using system font, which returns a value that is equal to font.lineHeight, not sure if this is a bug in the native API or that is intentional for some reason

I was able to workaround this by rounding the height we get from usedRectForTextContainer using the font.lineHeight and multiplying it by the number of lines (trying to mimic system font behaviour), something like
CGFloat(Int(height / font.lineHeight)) * font.lineHeight

GothamNarrow-Medium.otf.zip

@pahmed pahmed added the Bug label Nov 6, 2019
@pahmed pahmed changed the title Baseline alignment does not work as expected with some font Baseline alignment does not work as expected with some font on iOS Nov 6, 2019
@react-native-bot react-native-bot added the Platform: iOS iOS applications. label Nov 6, 2019
@pahmed
Copy link
Author

pahmed commented Nov 7, 2019

Update

I think I found the cause of the problem, the issue is that this font has a property leading set to 10 when you use font size 50, I think a simple solution for is to set usesFontLeading to NO

From the documentation:

// By default, a layout manager will use leading as specified by the font. However, this is not appropriate for most UI text, for which a fixed leading is usually specified by UI layout guidelines. These methods allow the use of the font's leading to be turned off.

Which probably explains how UILabel calculate the height for the text container

pahmed pushed a commit to pahmed/react-native that referenced this issue Nov 11, 2019
…where text is not aligned correctly for some fonts (with `leading` not equal to zero) facebook#27137
osdnk pushed a commit to osdnk/react-native that referenced this issue Mar 9, 2020
…alignment issue on some fonts (facebook#27195)

Summary:
Fixes facebook#27137

This PR fixes an issue on iOS where RCTTextView height is not calculated as it should for some fonts where font `leading` attributed is not equal to zero, which results in wrong baseline alignment behaviour.

The fix for this is by setting `usesFontLeading` property of `NSLayoutManager` to `NO`, which results is a layout behavior that is similar to `UILabel`

Probably the documentation for `usesFontLeading` describes why UILabel has a different (correct) layout behavior in that case
> // By default, a layout manager will use leading as specified by the font.  However, this is not appropriate for most UI text, for which a fixed leading is usually specified by UI layout guidelines.  These methods allow the use of the font's leading to be turned off.

## Changelog

[iOS] [Fixed] - Fix RCTTextView layout issue that happens on some font with `leading` attribute not equal to zero, which causes wrong base-alignment layout
Pull Request resolved: facebook#27195

Test Plan:
Below are the test results before and after the change, and comparing that to native UILabel behavior.

The test is done with using system font and custom font (`GothamNarrow-Medium`) and font size 50

[GothamNarrow-Medium.otf.zip](https://github.com/facebook/react-native/files/3832143/GothamNarrow-Medium.otf.zip)

```js
const App: () => React$Node = () => {
  return (
    <View style={{flex: 1, margin: 40, flexDirection: 'row', justifyContent: 'center', alignItems: 'baseline'}}>
      <View style={{width: 30, height: 30, backgroundColor: 'lightgray'}} />
      <Text style={{fontSize: 50, backgroundColor: 'green', fontFamily: 'GothamNarrow-Medium'}}>{'Settings'}</Text>
    </View>
  );
};
```

-------
### Before the fix

<img width="962" alt="Screenshot 2019-11-11 at 16 53 26" src="https://user-images.githubusercontent.com/5355138/68601049-dd778780-04a3-11ea-879e-cc7b4eb2af95.png">

-----
### After the fix
<img width="944" alt="Screenshot 2019-11-11 at 16 55 11" src="https://user-images.githubusercontent.com/5355138/68601180-1d3e6f00-04a4-11ea-87bc-61c6fa2cdb18.png">

-----
### Using `UILabel`
<img width="805" alt="Screenshot 2019-11-11 at 16 59 28" src="https://user-images.githubusercontent.com/5355138/68601487-b2d9fe80-04a4-11ea-9a0f-c025c7753c24.png">

Differential Revision: D19576556

Pulled By: shergin

fbshipit-source-id: 4eaafdab963c3f53c461884c581e205e6426718a
@facebook facebook locked as resolved and limited conversation to collaborators Oct 2, 2021
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Oct 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Platform: iOS iOS applications. Resolution: Locked This issue was locked by the bot.
Projects
None yet
2 participants