react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Fix TextInput vertical alignment issue when using lineHeight prop on iOS without changing Text baseline (Paper - old arch)

Open fabOnReact opened this issue 2 years ago • 1 comments

Summary:

This PR fixes visual regression introduced with https://github.com/facebook/react-native/pull/37465#issuecomment-1625110372. The baseline is set with RCTBaseTextInputView#enforceTextAttributesIfNeeded instead of RCTTextAttributes#effectiveParagraphStyle. RCTTextAttributes#effectiveParagraphStyle is shared between Text and TextInput. The regression was caused by changing the baseline of the Text with effectiveParagraphStyle, which does not need adjustment as it correctly aligns with lineHeight.

Summary from PR https://github.com/facebook/react-native/pull/37465

Adding paragraphStyle.maximumLineHeight to a iOS UITextField displays the text under the UITextField (ios-screenshot-1, ios-screenshot-2, ios-screenshot-3). The issue reproduces on Storyboard App (iOS) using UITextField and paragraphStyle.maximumLineHeight. It is not caused by react-native.

The issue is caused by a private class _UITextLayoutFragmentView (a CALayer children of UITextField), which assumes the wrong position when setting the lineHeight. _UITextLayoutFragmentView frame's y coordinates are set to 30, instead of 0 (react-native-screenshot-1, react-native-screenshot-2)

  • The _UITextLayoutFragmentView layer does not correctly position
  • The issue is caused by adding paragraphStyle.maximumLineHeight to UITextField.attributedText
  • The parent UITextField bounds do not correctly position child _UITextLayoutFragmentView. The issue causes the below text Sdfsd to display under the TextInput.

I was able to fix the issue and correctly align the private layout view _UITextLayoutFragmentView using the public API RCTUITextField textRectForBound, which allows modifying the UITextField frame and inset. The solution consists in the following steps, applied only to UITextField with lineHeight prop:

  1. set _UITextLayoutFragmentView to vertically align to the top. Required to correctly align _UITextLayoutFragmentView.
  2. apply custom bounds with RCTUITextField textRectForBound to align _UITextLayoutFragmentView with the correct y coordinates and height
  3. Adjust text baseline to be center aligned

fixes https://github.com/facebook/react-native/issues/28012 fixes https://github.com/facebook/react-native/issues/33986 Related https://github.com/facebook/react-native/issues/35741 https://github.com/facebook/react-native/issues/31112

Changelog:

[IOS] [FIXED] - Fix TextInput vertical alignment issue when using lineHeight prop on iOS without changing Text baseline (Paper - old arch)

Test Plan:

Extensive test included in the PR comments https://github.com/facebook/react-native/pull/37465#issuecomment-1551459879

fabOnReact avatar Jul 08 '23 11:07 fabOnReact

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 9,001,853 -62
android hermes armeabi-v7a 8,255,908 -72
android hermes x86 9,510,993 -63
android hermes x86_64 9,356,875 -66
android jsc arm64-v8a 9,614,916 -33
android jsc armeabi-v7a 8,741,553 -31
android jsc x86 9,701,866 -38
android jsc x86_64 9,948,428 -32

Base commit: 50f620a1ad8e925a46d2aa8e6439d5ce6f4f2ce4 Branch: main

analysis-bot avatar Jul 08 '23 11:07 analysis-bot