Skip to content

Commit e021e50

Browse files
dominictbfacebook-github-bot
authored andcommitted
fix: scroll the cursor into view when focus (#46411)
Summary: Currently in iOS, when focusing the multiline text input, the cursor is not automatically scrolled into view if it is out of view. This PR adds the small util to scroll the cursor into view when the text input focuses. This doesn't happen in Android due to [this](https://github.com/facebook/react-native/blob/defb0bd137711d3e76514d9202005a221a345871/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java#L360) Original issue: Expensify/App#48122 Original proposal: Expensify/App#48122 (comment) ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [IOS] [ADDED] - Scroll the cursor into view when text input is focused Pull Request resolved: #46411 Test Plan: Code to reproduce in rn-tester ``` const TextInputWithFocusButton = () => { const inputToFocusRef = React.useRef<React.ElementRef<typeof TextInput> | null>(null); return ( <View> <ExampleTextInput ref={inputToFocusRef} placeholder="height increases with content" defaultValue="React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about - learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native." multiline={true} enablesReturnKeyAutomatically={true} returnKeyType="go" style={[styles.multiline, styles.multilineExpandable]} /> <Button title="Focus" onPress={() => { inputToFocusRef.current?.focus(); }} /> </View> ); }; ``` Steps: - Move the cursor of the input to end of the input text - Scroll up the input - Blur the input - Click on `Focus` button to re-focus the input Note that before this fix, the cursor is not scrolled into view - In iOS <table> <tr> <th>Before</th> <th>After</th> </tr> <tr> <td> https://github.com/user-attachments/assets/de589cbf-158c-4e28-81d6-8412bf05ab23 </td> <td> https://github.com/user-attachments/assets/81c571f9-653b-49a5-9ecb-6eeaa2c54ec7 </td> </tr> </table> Reviewed By: sammy-SC Differential Revision: D62847985 Pulled By: cipolleschi fbshipit-source-id: c0367a7fc0a7a16b30c4538e59f42d971d959357
1 parent 794154e commit e021e50

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ - (void)didMoveToWindow
114114
const auto &props = static_cast<const TextInputProps &>(*_props);
115115
if (props.autoFocus) {
116116
[_backedTextInputView becomeFirstResponder];
117+
[self scrollCursorIntoView];
117118
}
118119
_didMoveToWindow = YES;
119120
[self initializeReturnKeyType];
@@ -495,6 +496,8 @@ - (void)focus
495496
[_backedTextInputView selectAll:nil];
496497
[self textInputDidChangeSelection];
497498
}
499+
500+
[self scrollCursorIntoView];
498501
}
499502

500503
- (void)blur
@@ -729,6 +732,16 @@ - (void)_updateTypingAttributes
729732
}
730733
}
731734

735+
- (void)scrollCursorIntoView
736+
{
737+
UITextRange *selectedRange = _backedTextInputView.selectedTextRange;
738+
if (selectedRange.empty) {
739+
NSInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
740+
toPosition:selectedRange.start];
741+
[_backedTextInputView scrollRangeToVisible:NSMakeRange(offsetStart, 0)];
742+
}
743+
}
744+
732745
- (void)_setMultiline:(BOOL)multiline
733746
{
734747
[_backedTextInputView removeFromSuperview];

packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const {
2828
Switch,
2929
Text,
3030
View,
31+
TextInput,
3132
} = require('react-native');
3233

3334
class WithLabel extends React.Component<$FlowFixMeProps> {
@@ -263,6 +264,31 @@ class AutogrowingTextInputExample extends React.Component<
263264
}
264265
}
265266

267+
const TextInputWithFocusButton = () => {
268+
const inputToFocusRef = React.useRef<React.ElementRef<
269+
typeof TextInput,
270+
> | null>(null);
271+
return (
272+
<View>
273+
<ExampleTextInput
274+
ref={inputToFocusRef}
275+
placeholder="height increases with content"
276+
defaultValue="React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about - learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native."
277+
multiline={true}
278+
enablesReturnKeyAutomatically={true}
279+
returnKeyType="go"
280+
style={[styles.multiline, styles.multilineExpandable]}
281+
/>
282+
<Button
283+
title="Focus"
284+
onPress={() => {
285+
inputToFocusRef.current?.focus();
286+
}}
287+
/>
288+
</View>
289+
);
290+
};
291+
266292
const styles = StyleSheet.create({
267293
multiline: {
268294
height: 50,
@@ -895,6 +921,12 @@ const textInputExamples: Array<RNTesterModuleExample> = [
895921
);
896922
},
897923
},
924+
{
925+
title: 'Auto scroll cursor into view when focusing',
926+
render: function (): React.Node {
927+
return <TextInputWithFocusButton />;
928+
},
929+
},
898930
{
899931
title: 'Line Break Mode',
900932
render: function (): React.Node {

0 commit comments

Comments
 (0)