From b0e39b2ed9b66b378eb75bee9e692fc801431ddd Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 20 Jul 2021 15:13:07 -0700 Subject: [PATCH] Maintain cursor position when text changes in text input Summary: Changelog: [internal] Cursor position needs to be calculated when attributed string changes. Reviewed By: JoshuaGross Differential Revision: D29786190 fbshipit-source-id: 99a42dc4d7c84e77c40f75bf4a9108d010bb1792 --- .../TextInput/RCTTextInputComponentView.mm | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index b7a76f07d2761e..9379c20cb6cb79 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -528,16 +528,21 @@ - (void)_updateState - (void)_setAttributedString:(NSAttributedString *)attributedString { - UITextRange *selectedRange = [_backedTextInputView selectedTextRange]; + UITextRange *selectedRange = _backedTextInputView.selectedTextRange; + NSInteger oldTextLength = _backedTextInputView.attributedText.string.length; if (![self _textOf:attributedString equals:_backedTextInputView.attributedText]) { _backedTextInputView.attributedText = attributedString; } - if (_lastStringStateWasUpdatedWith.length == attributedString.length) { - // Calling `[_backedTextInputView setAttributedText]` moves caret - // to the end of text input field. This cancels any selection as well - // as position in the text input field. In case the length of string - // doesn't change, selection and caret position is maintained. - [_backedTextInputView setSelectedTextRange:selectedRange notifyDelegate:NO]; + if (selectedRange.empty) { + // Maintaining a cursor position relative to the end of the old text. + NSInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument + toPosition:selectedRange.start]; + NSInteger offsetFromEnd = oldTextLength - offsetStart; + NSInteger newOffset = attributedString.string.length - offsetFromEnd; + UITextPosition *position = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument + offset:newOffset]; + [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position] + notifyDelegate:YES]; } _lastStringStateWasUpdatedWith = attributedString; }