Reputation: 7417
Does anyone have any ideas on how to get the caret position of a TextInput
? I tried onSelectionChange
and creating an event emitter from DocumentSelectionState
but neither appear to be working (they don't fire anything, no matter what I select).
For Example: https://rnplay.org/apps/eZnvIA
Upvotes: 20
Views: 20473
Reputation: 25864
If you're willing to get semi-adventurous, you can update the react-native
library to allow retrieving the cursor (x/y) coordinates (relative to the TextInput
), in the onSelectionChange
event parameter.
Full instructions for IOS AND Android. (Disclaimer: I did not come up with this solution - @Palisand did)
node_modules/react-native/Libraries/Text/RCTTextSelection.h
/**
* Object containing information about a TextInput's selection.
*/
@interface RCTTextSelection : NSObject
@property (nonatomic, assign, readonly) NSInteger start;
@property (nonatomic, assign, readonly) NSInteger end;
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@property (nonatomic, assign, readonly) CGPoint cursorPosition;
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end cursorPosition:(CGPoint)cursorPosition;
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
@end
node_modules/react-native/Libraries/Text/RCTTextSelection.m
@implementation RCTTextSelection
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end cursorPosition:(CGPoint)cursorPosition
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
{
if (self = [super init]) {
_start = start;
_end = end;
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
_cursorPosition = cursorPosition;
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
}
return self;
}
@end
@implementation RCTConvert (RCTTextSelection)
+ (RCTTextSelection *)RCTTextSelection:(id)json
{
if ([json isKindOfClass:[NSDictionary class]]) {
NSInteger start = [self NSInteger:json[@"start"]];
NSInteger end = [self NSInteger:json[@"end"]];
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
CGPoint cursorPosition = CGPointMake(
[self CGFloat:json[@"cursorPositionX"]],
[self CGFloat:json[@"cursorPositionY"]]
);
return [[RCTTextSelection alloc] initWithStart:start
end:end
cursorPosition:cursorPosition];
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
}
return nil;
}
@end
node_modules/react-native/Libraries/Text/RCTTextInput.m
- (RCTTextSelection *)selection
{
id<RCTBackedTextInputViewProtocol> backedTextInput = self.backedTextInputView;
UITextRange *selectedTextRange = backedTextInput.selectedTextRange;
return [[RCTTextSelection new] initWithStart:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.start]
end:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.end]
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
cursorPosition:[backedTextInput caretRectForPosition:selectedTextRange.start].origin];
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
}
...
- (void)textInputDidChangeSelection
{
if (!_onSelectionChange) {
return;
}
RCTTextSelection *selection = self.selection;
_onSelectionChange(@{
@"selection": @{
@"start": @(selection.start),
@"end": @(selection.end),
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@"cursorPositionX": @(selection.cursorPosition.x),
@"cursorPositionY": @(selection.cursorPosition.y)
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
},
});
}
Upvotes: 1
Reputation: 649
In order to get the position of the cursor the only thing you need is onSelectionChange https://reactnative.dev/docs/textinput#onselectionchange. The function will return the start and the end values.
Upvotes: 2
Reputation: 658
In react-native <=v0.56 following code get the selection as an object of {state: number, end: number}. The text positions start from 0. For example selection like {start: 0, end: 0} can happen and it means your curser is placed before first character of you text and nothing selected. Another selection type happens when start and end is not equal, like {start: 0, end: 10}, this means you select first 10 character of your text.
By the way as following issue if you use react native v0.57.0 or v0.57.1 in the textInput must be add multiline={true} to everything work properly.
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TextInput} from 'react-native';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props)
this.state = {
selection: {start: 0, end: 0},
text: 'This is a sample text',
}
}
render() {
return (
<View style={styles.container}>
<Text>{this.state.selection.start}, {this.state.selection.end}</Text>
<TextInput
style={{ width: 300, borderColor: 'gray', borderWidth: 1 }}
onSelectionChange={({ nativeEvent: { selection } }) => {
this.setState({ selection })
}}
onChangeText={(text) => this.setState({ text })}
value={this.state.text}
/>
</View>
);
}
}
The result is:
Upvotes: 5
Reputation: 2466
onSelectionChange={(event) => console.log(event.nativeEvent.selection)}
Upvotes: 31
Reputation: 3238
As a start, you probably want to use the onChange or even onChangeText event instead. That will get your alert firing.
Upvotes: -5