Implements `onKeyPress`
Summary: - When a key is pressed, it's `key value` is passed as an argument to the callback handler. - For `Enter` and `Backspace` keys, I'm using their `key value` as defined [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#Key_values). As per JonasJonny & brentvatne's [suggestion](https://github.com/facebook/react-native/issues/1882#issuecomment-123485883). - Example ```javascript _handleKeyPress: function(e) { console.log(e.nativeEvent.key); }, render: function() { return ( <View style={styles.container}> <TextInput style={{width: 150, height: 25, borderWidth: 0.5}} onKeyPress={this._handleKeyPress} /> <TextInput style={{width: 150, height: 100, borderWidth: 0.5}} onKeyPress={this._handleKeyPress} multiline={true} /> </View> ); } ``` - Implements [shouldChangeCharactersInRange](https://developer.apple.com/library/prerelease/ios/documentat Closes https://github.com/facebook/react-native/pull/2082 Reviewed By: javache Differential Revision: D2280460 Pulled By: nicklockwood fb-gh-sync-id: 1f824f80649043dc2520c089e2531d428d799405
This commit is contained in:
parent
6539b26810
commit
6c7c845145
|
@ -42,6 +42,7 @@ var TextEventsExample = React.createClass({
|
|||
curText: '<No Event>',
|
||||
prevText: '<No Event>',
|
||||
prev2Text: '<No Event>',
|
||||
prev3Text: '<No Event>',
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -51,6 +52,7 @@ var TextEventsExample = React.createClass({
|
|||
curText: text,
|
||||
prevText: state.curText,
|
||||
prev2Text: state.prevText,
|
||||
prev3Text: state.prev2Text,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -73,12 +75,16 @@ var TextEventsExample = React.createClass({
|
|||
onSubmitEditing={(event) => this.updateText(
|
||||
'onSubmitEditing text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onKeyPress={(event) => {
|
||||
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
|
||||
}}
|
||||
style={styles.default}
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
(prev: {this.state.prevText}){'\n'}
|
||||
(prev2: {this.state.prev2Text})
|
||||
(prev2: {this.state.prev2Text}){'\n'}
|
||||
(prev3: {this.state.prev3Text})
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -217,6 +217,13 @@ var TextInput = React.createClass({
|
|||
* Callback that is called when the text input's submit button is pressed.
|
||||
*/
|
||||
onSubmitEditing: PropTypes.func,
|
||||
/**
|
||||
* Callback that is called when a key is pressed.
|
||||
* Pressed key value is passed as an argument to the callback handler.
|
||||
* Fires before onChange callbacks.
|
||||
* @platform ios
|
||||
*/
|
||||
onKeyPress: PropTypes.func,
|
||||
/**
|
||||
* Invoked on mount and layout changes with `{x, y, width, height}`.
|
||||
*/
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
@property (nonatomic, strong) UIColor *placeholderTextColor;
|
||||
@property (nonatomic, assign) NSInteger mostRecentEventCount;
|
||||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
@property (nonatomic, assign) BOOL textWasPasted;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)textFieldDidChange;
|
||||
- (void)sendKeyValueForString:(NSString *)string;
|
||||
|
||||
@end
|
||||
|
|
|
@ -39,6 +39,23 @@
|
|||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
|
||||
- (void)sendKeyValueForString:(NSString *)string
|
||||
{
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
|
||||
reactTag:self.reactTag
|
||||
text:nil
|
||||
key:string
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
// This method is overriden for `onKeyPress`. The manager
|
||||
// will not send a keyPress for text that was pasted.
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
_textWasPasted = YES;
|
||||
[super paste:sender];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
|
@ -134,6 +151,7 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
|
||||
reactTag:self.reactTag
|
||||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
|
@ -142,6 +160,7 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||
reactTag:self.reactTag
|
||||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
- (void)textFieldSubmitEditing
|
||||
|
@ -149,6 +168,7 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
|
||||
reactTag:self.reactTag
|
||||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
|
@ -162,6 +182,7 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
|
||||
reactTag:self.reactTag
|
||||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
|
@ -181,6 +202,7 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
|
||||
reactTag:self.reactTag
|
||||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -31,6 +31,13 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (BOOL)textField:(RCTTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
||||
{
|
||||
// Only allow single keypresses for onKeyPress, pasted text will not be sent.
|
||||
if (textField.textWasPasted) {
|
||||
textField.textWasPasted = NO;
|
||||
} else {
|
||||
[textField sendKeyValueForString:string];
|
||||
}
|
||||
|
||||
if (textField.maxLength == nil || [string isEqualToString:@"\n"]) { // Make sure forms can be submitted via return
|
||||
return YES;
|
||||
}
|
||||
|
@ -54,6 +61,14 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
}
|
||||
|
||||
// This method allows us to detect a `Backspace` keyPress
|
||||
// even when there is no more text in the TextField
|
||||
- (BOOL)keyboardInputShouldDelete:(RCTTextField *)textField
|
||||
{
|
||||
[self textField:textField shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:@""];
|
||||
return YES;
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(caretHidden, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(editable, enabled, BOOL)
|
||||
|
|
|
@ -14,6 +14,22 @@
|
|||
#import "RCTUtils.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@interface RCTUITextView : UITextView
|
||||
|
||||
@property (nonatomic, assign) BOOL textWasPasted;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUITextView
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
_textWasPasted = YES;
|
||||
[super paste:sender];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTextView
|
||||
{
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
|
@ -33,7 +49,7 @@
|
|||
_eventDispatcher = eventDispatcher;
|
||||
_placeholderTextColor = [self defaultPlaceholderTextColor];
|
||||
|
||||
_textView = [[UITextView alloc] initWithFrame:self.bounds];
|
||||
_textView = [[RCTUITextView alloc] initWithFrame:self.bounds];
|
||||
_textView.backgroundColor = [UIColor clearColor];
|
||||
_textView.scrollsToTop = NO;
|
||||
_textView.delegate = self;
|
||||
|
@ -56,15 +72,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
// first focused.
|
||||
UIEdgeInsets adjustedFrameInset = UIEdgeInsetsZero;
|
||||
adjustedFrameInset.left = _contentInset.left - 5;
|
||||
|
||||
|
||||
UIEdgeInsets adjustedTextContainerInset = _contentInset;
|
||||
adjustedTextContainerInset.top += 5;
|
||||
adjustedTextContainerInset.left = 0;
|
||||
|
||||
|
||||
CGRect frame = UIEdgeInsetsInsetRect(self.bounds, adjustedFrameInset);
|
||||
_textView.frame = frame;
|
||||
_placeholderView.frame = frame;
|
||||
|
||||
|
||||
_textView.textContainerInset = adjustedTextContainerInset;
|
||||
_placeholderView.textContainerInset = adjustedTextContainerInset;
|
||||
}
|
||||
|
@ -138,8 +154,18 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
return _textView.text;
|
||||
}
|
||||
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
- (BOOL)textView:(RCTUITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
if (textView.textWasPasted) {
|
||||
textView.textWasPasted = NO;
|
||||
} else {
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
|
||||
reactTag:self.reactTag
|
||||
text:nil
|
||||
key:text
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
if (_maxLength == nil) {
|
||||
return YES;
|
||||
}
|
||||
|
@ -215,6 +241,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
|
||||
reactTag:self.reactTag
|
||||
text:textView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
|
@ -225,6 +252,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
|
||||
reactTag:self.reactTag
|
||||
text:textView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
}
|
||||
|
@ -234,6 +262,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||
reactTag:self.reactTag
|
||||
text:textView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
|
@ -253,6 +282,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
|
||||
reactTag:self.reactTag
|
||||
text:_textView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -16,7 +16,8 @@ typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
|||
RCTTextEventTypeBlur,
|
||||
RCTTextEventTypeChange,
|
||||
RCTTextEventTypeSubmit,
|
||||
RCTTextEventTypeEnd
|
||||
RCTTextEventTypeEnd,
|
||||
RCTTextEventTypeKeyPress
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||
|
@ -95,6 +96,7 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName);
|
|||
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text
|
||||
key:(NSString *)key
|
||||
eventCount:(NSInteger)eventCount;
|
||||
|
||||
/**
|
||||
|
|
|
@ -144,6 +144,7 @@ RCT_EXPORT_MODULE()
|
|||
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text
|
||||
key:(NSString *)key
|
||||
eventCount:(NSInteger)eventCount
|
||||
{
|
||||
static NSString *events[] = {
|
||||
|
@ -152,16 +153,36 @@ RCT_EXPORT_MODULE()
|
|||
@"change",
|
||||
@"submitEditing",
|
||||
@"endEditing",
|
||||
@"keyPress"
|
||||
};
|
||||
|
||||
[self sendInputEventWithName:events[type] body:text ? @{
|
||||
@"text": text,
|
||||
@"eventCount": @(eventCount),
|
||||
@"target": reactTag
|
||||
} : @{
|
||||
NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
|
||||
@"eventCount": @(eventCount),
|
||||
@"target": reactTag
|
||||
}];
|
||||
|
||||
if (text) {
|
||||
body[@"text"] = text;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
if (key.length == 0) {
|
||||
key = @"Backspace"; // backspace
|
||||
} else {
|
||||
switch ([key characterAtIndex:0]) {
|
||||
case '\t':
|
||||
key = @"Tab";
|
||||
break;
|
||||
case '\n':
|
||||
key = @"Enter";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
body[@"key"] = key;
|
||||
}
|
||||
|
||||
[self sendInputEventWithName:events[type] body:body];
|
||||
}
|
||||
|
||||
- (void)sendEvent:(id<RCTEvent>)event
|
||||
|
|
|
@ -80,6 +80,7 @@ RCT_EXPORT_MODULE()
|
|||
@"blur",
|
||||
@"submitEditing",
|
||||
@"endEditing",
|
||||
@"keyPress",
|
||||
|
||||
// Touch events
|
||||
@"touchStart",
|
||||
|
|
Loading…
Reference in New Issue