diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index d02e003a439e410ddab597b2c036256c80db0395..cf745cf37a37d34f9e93eb8df10b43cb54ab91a3 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -382,6 +382,7 @@ _backedTextInputView.inputAccessoryView = nil; _hasInputAccessoryView = false; [_backedTextInputView resignFirstResponder]; + [_backedTextInputView resetDictationRecognition]; } #pragma mark - RCTBackedTextInputDelegate @@ -703,6 +704,7 @@ static NSSet *returnKeyTypesSet; { return { .text = RCTStringFromNSString(_backedTextInputView.attributedText.string), + .isComposing = _backedTextInputView.markedTextRange != nil, .selectionRange = [self _selectionRange], .eventCount = static_cast(_mostRecentEventCount), .contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset), @@ -865,4 +867,6 @@ static NSSet *returnKeyTypesSet; + // Preserve the native recognition placeholder while iOS finalizes dictation. BOOL shouldFallbackToBareTextComparison = [_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"dictation"] || + _backedTextInputView.dictationRecognizing || [_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"ko-KR"] || _backedTextInputView.markedTextRange || _backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem; diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp index a9bc219f8bf729111e907423ca61b991fd712b46..60e9c6f54edb295295d6e48d1d554499106b7e57 100644 --- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp @@ -24,6 +24,10 @@ static jsi::Value textInputMetricsPayload( payload.setProperty(runtime, "eventCount", textInputMetrics.eventCount); + if (textInputMetrics.isComposing.has_value()) { + payload.setProperty(runtime, "isComposing", *textInputMetrics.isComposing); + } + if (includeSelectionState) { auto selection = jsi::Object(runtime); selection.setProperty( diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h index a3f2e01c130c1a53aee0b39e72927d21f2a6973d..c9d34f8b42644800b5c85e5be45b6ebfbe94a54d 100644 --- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h +++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h @@ -7,6 +7,8 @@ #pragma once +#include + #include #include @@ -18,6 +20,7 @@ class TextInputEventEmitter : public ViewEventEmitter { struct Metrics { std::string text; + std::optional isComposing; AttributedString::Range selectionRange; // ScrollView-like metrics Size contentSize; diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index 79ee7ffe43..956f189a1c 100644 --- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -136,6 +136,7 @@ using namespace facebook::react; - (void)prepareForRecycle { [super prepareForRecycle]; _textView.state = nullptr; + _textView.layoutMetrics = EmptyLayoutMetrics; _accessibilityProvider = nil; } diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h --- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -48,6 +48,7 @@ - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE; - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate; - (void)scrollRangeToVisible:(NSRange)selectedTextRange; +- (void)resetDictationRecognition; // This protocol disallows direct access to `text` property because // unwise usage of it can break the `attributeText` behavior. diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm --- a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm +++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm @@ -197,6 +197,11 @@ } #pragma mark - Dictation + +- (void)resetDictationRecognition +{ + _dictationRecognizing = NO; +} - (void)dictationRecordingDidEnd { diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm @@ -319,6 +319,11 @@ #pragma mark - Dictation +- (void)resetDictationRecognition +{ + _dictationRecognizing = NO; +} + - (void)dictationRecordingDidEnd { _dictationRecognizing = YES;