diff --git a/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm index eef87f87f632..b9243ae21022 100644 --- a/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm +++ b/packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm @@ -396,6 +396,9 @@ - (void)_updatePlaceholder _placeholderView.attributedText = [[NSAttributedString alloc] initWithString:_placeholder ?: @"" attributes:[self _placeholderTextAttributes]]; [self _invalidatePlaceholderVisibility]; + // `layoutSubviews` sizes the placeholder label to fit its text, and the new text may need a different size. Nothing + // else lays the label out again when this view keeps its size (also after it is recycled). + [self setNeedsLayout]; } - (NSDictionary *)_placeholderTextAttributes diff --git a/packages/react-native/React/Tests/Text/RCTUITextViewTests.mm b/packages/react-native/React/Tests/Text/RCTUITextViewTests.mm new file mode 100644 index 000000000000..1068ff9415cb --- /dev/null +++ b/packages/react-native/React/Tests/Text/RCTUITextViewTests.mm @@ -0,0 +1,51 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +static UILabel *RCTPlaceholderLabel(RCTUITextView *textView) +{ + for (UIView *subview in textView.subviews) { + if ([subview isKindOfClass:[UILabel class]]) { + return (UILabel *)subview; + } + } + return nil; +} + +@interface RCTUITextViewTests : XCTestCase +@end + +@implementation RCTUITextViewTests + +- (void)testPlaceholderIsLaidOutWhenSetOnALaidOutView +{ + RCTUITextView *textView = [[RCTUITextView alloc] initWithFrame:CGRectMake(0, 0, 340, 122)]; + [textView layoutIfNeeded]; + + textView.placeholder = @"Type 5 characters to show length helper"; + [textView layoutIfNeeded]; + + XCTAssertNotNil(RCTPlaceholderLabel(textView)); + XCTAssertGreaterThan(CGRectGetHeight(RCTPlaceholderLabel(textView).frame), 0); +} + +- (void)testPlaceholderIsLaidOutWhenItGetsLongerOnALaidOutView +{ + RCTUITextView *textView = [[RCTUITextView alloc] initWithFrame:CGRectMake(0, 0, 340, 122)]; + textView.placeholder = @"Short placeholder"; + [textView layoutIfNeeded]; + CGFloat oneLineHeight = CGRectGetHeight(RCTPlaceholderLabel(textView).frame); + + textView.placeholder = @"Tell us why you're interested in this position and what you would bring to the team"; + [textView layoutIfNeeded]; + + XCTAssertGreaterThan(CGRectGetHeight(RCTPlaceholderLabel(textView).frame), oneLineHeight); +} + +@end