Skip to content

Commit a8cf4e7

Browse files
javachemeta-codesync[bot]
authored andcommitted
Defer transformed TextInput edits outside UIKit delegate callbacks (#59239)
Summary: Pull Request resolved: #59239 UIKit text change delegate callbacks now remain decision-only when JavaScript transforms an edit. The adapter rejects the original UIKit edit, records an implementation-private transaction, and applies an incremental `UITextInput` replacement on the next main-queue turn after validating the backing-view generation and base text. This preserves partial `maxLength` truncation without synchronously replacing the full attributed string or selection. Marked-text composition remains UIKit-owned; an over-limit committed value is corrected through the same deferred transaction path after composition ends. A runtime gate controls the new path. Stale edits are discarded when the backing text changes, lifecycle and JavaScript-driven updates invalidate queued transactions, and successful commits emit one change update plus the corresponding selection event. On tvOS, the deferred drain remains disabled because multiline text views are not editable. Changelog: [iOS][Fixed] - Preserve transformed `TextInput` edits without synchronously rewriting text inside UIKit delegate callbacks Differential Revision: D126438495
1 parent 0bd3326 commit a8cf4e7

26 files changed

Lines changed: 841 additions & 119 deletions

‎packages/react-native/Libraries/Text/React-RCTText.podspec‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
3434

3535
s.dependency "Yoga"
3636
s.dependency "React-Core/RCTTextHeaders", version
37+
add_dependency(s, "React-featureflags")
3738

3839
mark_as_react_native_build(s)
3940
end

‎packages/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
#import <React/RCTBackedTextInputDelegateAdapter.h>
1414

15+
@interface RCTBackedTextViewDelegateAdapter (PendingTextInputEdit)
16+
- (void)invalidatePendingTextInputEdit;
17+
- (void)schedulePendingTextInputEditWithRange:(NSRange)range replacementText:(NSString *)replacementText;
18+
@end
19+
1520
@implementation RCTUITextView {
1621
UILabel *_placeholderView;
1722
UITextView *_detachedTextView;
@@ -207,6 +212,16 @@ - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BO
207212
[super setSelectedTextRange:selectedTextRange];
208213
}
209214

215+
- (void)rct_invalidatePendingTextInputEdit
216+
{
217+
[_textInputDelegateAdapter invalidatePendingTextInputEdit];
218+
}
219+
220+
- (void)rct_enqueueTextInputEditWithRange:(NSRange)range replacementText:(NSString *)replacementText
221+
{
222+
[_textInputDelegateAdapter schedulePendingTextInputEditWithRange:range replacementText:replacementText];
223+
}
224+
210225
// After restoring the previous cursor position, we manually trigger the scroll to the new cursor position (PR 38679).
211226
- (void)scrollRangeToVisible:(NSRange)range
212227
{

0 commit comments

Comments
 (0)