fix(ios): center TextInput text when lineHeight > fontSize on Fabric#56487
Draft
janicduplessis wants to merge 2 commits intofacebook:mainfrom
Draft
fix(ios): center TextInput text when lineHeight > fontSize on Fabric#56487janicduplessis wants to merge 2 commits intofacebook:mainfrom
janicduplessis wants to merge 2 commits intofacebook:mainfrom
Conversation
606f1d7 to
4e79973
Compare
…ht > fontSize On iOS, when a TextInput's lineHeight exceeds its font's line height, UIKit anchors glyphs to the bottom of the attributed-string line box instead of centering them within it. The same misalignment affects the placeholder. On single-line UITextField the caret is also sized to the full line box. This patch fixes all three surfaces. The approach varies by UIKit rendering path: UITextView (multi-line) typed text — honors NSBaselineOffsetAttributeName. Call RCTApplyBaselineOffset in RCTTextInputComponentView._setAttributedString: to inject the offset. Re-seed NSParagraphStyleAttributeName from defaultTextAttributes on ranges missing it, because UIKit's typingAttributes drops the paragraph style between keystrokes and _updateState round-trips the stripped attributedText through TextInputState — without the re-seed the helper sees maximumLineHeight == 0 and bails for typed content. Placeholder on both UITextField.attributedPlaceholder (UILabel draw) and RCTUITextView._placeholderView — both honor NSBaselineOffsetAttributeName. Add the offset computation to _placeholderTextAttributes on both backing views. The fix applies to both Paper and Fabric because the backing views are shared. UITextField (single-line) typed text — the UIFieldEditor draw path does NOT honor NSBaselineOffsetAttributeName, and it sizes the caret to the paragraph-style line box height. Override setAttributedText: to forward a copy with paragraphStyle's minimumLineHeight/maximumLineHeight zeroed out (and NSBaselineOffsetAttributeName removed) to super. UITextField then renders the line at the font's natural height and its built-in vertical centering positions it correctly in the bounds; the caret rect likewise shrinks to the natural font height. _defaultTextAttributes (the local ivar) keeps the unmodified paragraph style so the placeholder path still sees the real lineHeight. Yoga's frame-height measurement is unaffected.
4e79973 to
bf72714
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On iOS, when a
TextInputhaslineHeight > fontSize, UIKit misrenders three surfaces:defaultTextAttributesand sits low, on both Paper and Fabric.The approach depends on which UIKit rendering path draws the affected surface:
UITextView (multi-line) typed text — honors
NSBaselineOffsetAttributeNameCall
RCTApplyBaselineOffsetinRCTTextInputComponentView._setAttributedString:to inject the offset. The helper already ships in Fabric (used by<Text>inRCTTextLayoutManager) but was never wired into the<TextInput>path; Paper fixed this in #38359.Wiring in the call alone is not enough: each keystroke round-trips UIKit → Fabric state → back, and UIKit's
typingAttributesdropsNSParagraphStyleAttributeName._updateStatestores the strippedattributedTextas an OpaquePointer inTextInputState, which the helper would otherwise read asmaximumLineHeight == 0and return early. Re-seed the paragraph style fromdefaultTextAttributeson ranges missing it before calling the helper.Placeholder on both backing views — the UILabel-based draw paths honor
NSBaselineOffsetAttributeNameAdd the equivalent offset computation to
_placeholderTextAttributesonRCTUITextField(used asattributedPlaceholder) andRCTUITextView(used asattributedTextof aUILabelsubview). Both backing views are shared between Paper and Fabric, so this single change covers both arches.UITextField (single-line) typed text — the
UIFieldEditordraw path does not honorNSBaselineOffsetAttributeNameInstead, override
setAttributedText:onRCTUITextFieldto forward a copy with paragraphStyle'sminimumLineHeight/maximumLineHeightzeroed out (andNSBaselineOffsetAttributeNameremoved) tosuper. UITextField then renders the line at the font's natural height and its built-in vertical centering positions the glyph correctly in the bounds. The caret rect, whichUITextFieldsizes from the same paragraph-style line box, shrinks to the natural font height as a consequence — no separate caret override is needed._defaultTextAttributes(the local ivar) keeps the unmodified paragraph style so the placeholder path still sees the reallineHeight. Yoga's frame-height measurement is also unaffected — this changes only whatUITextFielduses for per-line rendering.Changelog
[IOS] [FIXED] - Center typed TextInput text, placeholder, and single-line caret when
lineHeight > fontSize.Test Plan
RN Tester → TextInput → lineHeight baseline renders one single-line and one multi-line
TextInputwithfontSize: 16, lineHeight: 32. Before: placeholder sits low, typed glyphs anchor to the bottom of the line box, single-line caret overshoots the glyph. After: placeholder and typed text render vertically centered; single-line caret matches the glyph height and sits alongside it.Paper typed-text behavior is unaffected (the Fabric hunk is isolated to
RCTTextInputComponentView._setAttributedString:, and theRCTUITextField.setAttributedText:override is dormant when no paragraphStyle line-height is set). Android is unaffected.Related: #38359, #39145, #53092.