diff --git a/packages/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/packages/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js index 1af4104f5f0..fe5c4913a83 100644 --- a/packages/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js +++ b/packages/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js @@ -142,6 +142,7 @@ const RCTTextInputViewConfig = { placeholder: true, autoCorrect: true, multiline: true, + numberOfLines: true, textContentType: true, maxLength: true, autoCapitalize: true, diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index c8dff5c5729..29bee1c07c1 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -1626,6 +1626,7 @@ function InternalTextInput(props: TextInputProps): React.Node { focusable={tabIndex !== undefined ? !tabIndex : focusable} mostRecentEventCount={mostRecentEventCount} nativeID={id ?? props.nativeID} + numberOfLines={props.rows ?? props.numberOfLines} onBlur={_onBlur} onChange={_onChange} onContentSizeChange={props.onContentSizeChange} diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index 9ffe117c4e1..ae68babaffc 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -232,6 +232,17 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi layoutManager.usesFontLeading = NO; [layoutManager addTextContainer:textContainer]; + // A workaround for the issue with empty line measurement: + // When maximumNumberOfLines is set to N and N+1 line is empty, the returned + // measurement is for N+1 lines. Adding any character to that line results + // in the correct measurement. + if (attributedString.length > 0 && [[attributedString string] characterAtIndex:attributedString.length - 1] == '\n') { + NSMutableAttributedString *mutableString = + [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; + [mutableString replaceCharactersInRange:NSMakeRange(attributedString.length - 1, 1) withString:@"\n "]; + attributedString = mutableString; + } + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; RCTApplyBaselineOffset(textStorage); diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index c04b37a6189..c2004ddcb87 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -304,7 +304,6 @@ function KeyboardShortcutsExample() { const styles = StyleSheet.create({ multiline: { height: 50, - marginBottom: 4, }, multilinePlaceholderStyles: { letterSpacing: 10, @@ -598,7 +597,7 @@ const textInputExamples: Array = [ title: 'Multiline', render: function (): React.Node { return ( - + = [ multiline={true} style={styles.multiline} /> +