diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 281c5ea88b53..9f523c63bbeb 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -395,10 +395,7 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics _backedTextInputView.textContainerInset = RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.contentInsets - layoutMetrics.borderWidth); - if (!CGSizeEqualToSize(_previousContentSize, _backedTextInputView.contentSize) && _eventEmitter) { - _previousContentSize = _backedTextInputView.contentSize; - static_cast(*_eventEmitter).onContentSizeChange([self _textInputMetrics]); - } + [self _emitContentSizeChangeIfNeeded]; } - (void)prepareForRecycle @@ -415,6 +412,7 @@ - (void)prepareForRecycle _backedTextInputView.inputAccessoryViewID = nil; _backedTextInputView.inputAccessoryView = nil; _hasInputAccessoryView = false; + _previousContentSize = CGSizeZero; [_backedTextInputView resignFirstResponder]; } @@ -565,6 +563,8 @@ - (void)textInputDidChange const auto &textInputEventEmitter = static_cast(*_eventEmitter); textInputEventEmitter.onChange([self _textInputMetrics]); } + + [self _emitContentSizeChangeAfterTextChange]; } - (void)textInputDidChangeSelection @@ -803,6 +803,30 @@ - (void)handleInputAccessoryDoneButton }; } +- (void)_emitContentSizeChangeAfterTextChange +{ + if (![self _hasEmittedContentSize]) { + return; + } + [_backedTextInputView layoutIfNeeded]; + [self _emitContentSizeChangeIfNeeded]; +} + +- (BOOL)_hasEmittedContentSize +{ + return !CGSizeEqualToSize(_previousContentSize, CGSizeZero); +} + +- (void)_emitContentSizeChangeIfNeeded +{ + CGSize contentSize = _backedTextInputView.contentSize; + if (CGSizeEqualToSize(_previousContentSize, contentSize) || !_eventEmitter) { + return; + } + _previousContentSize = contentSize; + static_cast(*_eventEmitter).onContentSizeChange([self _textInputMetrics]); +} + - (void)_updateState { if (!_state) { @@ -881,6 +905,7 @@ - (void)_setAttributedString:(NSAttributedString *)attributedString [self _restoreTextSelectionAndIgnoreCaretChange:YES]; [self _updateTypingAttributes]; _lastStringStateWasUpdatedWith = attributedString; + [self _emitContentSizeChangeAfterTextChange]; } // Ensure that newly typed text will inherit any custom attributes. We follow the logic of RN Android, where attributes diff --git a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js index 795ea66911ea..75448f58a599 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js @@ -1192,6 +1192,19 @@ module.exports = [ ); }, }, + { + title: 'Content size change with fixed height', + name: 'contentSizeChangeFixedHeight', + render: function (): React.Node { + return ( + + + + ); + }, + }, { title: 'Drag and drop', render: function (): React.Node {