From 97c7c6a4b6910062dba96a4f4e2b82c1cef4b9ea Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Fri, 13 Jan 2023 09:34:03 -0800 Subject: [PATCH] fix: dispatch ContentSizeChange event on Fabric on iOS (#35816) Summary: On Fabric, `onContentSizeChange` of `TextInput` component was never fired on `iOS`, since the logic dispatching it was implemented in `RCTBaseTextInputShadowView` on Paper: https://github.com/facebook/react-native/blob/0f8dc067ac079f7b14696cfcafa37e3ec19a0409/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m#L105. This class is not used on Fabric, therefore the event was never dispatched. On Paper, it was dispatched in `dirtyLayout` method, so I added dispatching of this event based on the change of content size in `layoutSubviews` method, since this method seems the closest one on Fabric. I am not sure if it is the best place for it though. ## Changelog [IOS] [ADDED] - dispatch `onContentSizeChange` event on Fabric. Pull Request resolved: https://github.com/facebook/react-native/pull/35816 Test Plan: Try to use `onContentSizeChange` callback in `TextInput` component: ```tsx import React from 'react'; import {TextInput, SafeAreaView} from 'react-native'; const App = () => { return ( console.log(e)} /> ); }; export default App; ``` Reviewed By: christophpurrer Differential Revision: D42499974 Pulled By: sammy-SC fbshipit-source-id: 3e010ff096cf91cb3e7b87ed2753e9d0e7be9650 --- .../TextInput/RCTTextInputComponentView.mm | 5 +++ .../iostextinput/TextInputEventEmitter.cpp | 32 ++++++++++++++++++- .../iostextinput/TextInputEventEmitter.h | 5 +++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 5aa7024c9743e2..156a913c435d6c 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -245,6 +245,11 @@ - (void)updateLayoutMetrics:(LayoutMetrics const &)layoutMetrics UIEdgeInsetsInsetRect(self.bounds, RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.borderWidth)); _backedTextInputView.textContainerInset = RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.contentInsets - layoutMetrics.borderWidth); + + if (_eventEmitter) { + auto const &textInputEventEmitter = *std::static_pointer_cast(_eventEmitter); + textInputEventEmitter.onContentSizeChange([self _textInputMetrics]); + } } - (void)prepareForRecycle diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp index 494ab15d396279..24a580276932ef 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp @@ -36,6 +36,23 @@ static jsi::Value textInputMetricsPayload( return payload; }; +static jsi::Value textInputMetricsContentSizePayload( + jsi::Runtime &runtime, + TextInputMetrics const &textInputMetrics) { + auto payload = jsi::Object(runtime); + + { + auto contentSize = jsi::Object(runtime); + contentSize.setProperty( + runtime, "width", textInputMetrics.contentSize.width); + contentSize.setProperty( + runtime, "height", textInputMetrics.contentSize.height); + payload.setProperty(runtime, "contentSize", contentSize); + } + + return payload; +}; + static jsi::Value keyPressMetricsPayload( jsi::Runtime &runtime, KeyPressMetrics const &keyPressMetrics) { @@ -82,7 +99,8 @@ void TextInputEventEmitter::onChangeSync( void TextInputEventEmitter::onContentSizeChange( TextInputMetrics const &textInputMetrics) const { - dispatchTextInputEvent("contentSizeChange", textInputMetrics); + dispatchTextInputContentSizeChangeEvent( + "contentSizeChange", textInputMetrics); } void TextInputEventEmitter::onSelectionChange( @@ -137,4 +155,16 @@ void TextInputEventEmitter::dispatchTextInputEvent( priority); } +void TextInputEventEmitter::dispatchTextInputContentSizeChangeEvent( + std::string const &name, + TextInputMetrics const &textInputMetrics, + EventPriority priority) const { + dispatchEvent( + name, + [textInputMetrics](jsi::Runtime &runtime) { + return textInputMetricsContentSizePayload(runtime, textInputMetrics); + }, + priority); +} + } // namespace facebook::react diff --git a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h index 3dc1cefaff52de..2e7e2f79ea993a 100644 --- a/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h +++ b/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h @@ -54,6 +54,11 @@ class TextInputEventEmitter : public ViewEventEmitter { std::string const &name, TextInputMetrics const &textInputMetrics, EventPriority priority = EventPriority::AsynchronousBatched) const; + + void dispatchTextInputContentSizeChangeEvent( + std::string const &name, + TextInputMetrics const &textInputMetrics, + EventPriority priority = EventPriority::AsynchronousBatched) const; }; } // namespace react