Skip to content

Commit 6c7c845

Browse files
dsibiskifacebook-github-bot-6
authored and
facebook-github-bot-6
committed
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](#1882 (comment)). - 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 #2082 Reviewed By: javache Differential Revision: D2280460 Pulled By: nicklockwood fb-gh-sync-id: 1f824f80649043dc2520c089e2531d428d799405
1 parent 6539b26 commit 6c7c845

File tree

9 files changed

+119
-12
lines changed

9 files changed

+119
-12
lines changed

Examples/UIExplorer/TextInputExample.ios.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var TextEventsExample = React.createClass({
4242
curText: '<No Event>',
4343
prevText: '<No Event>',
4444
prev2Text: '<No Event>',
45+
prev3Text: '<No Event>',
4546
};
4647
},
4748

@@ -51,6 +52,7 @@ var TextEventsExample = React.createClass({
5152
curText: text,
5253
prevText: state.curText,
5354
prev2Text: state.prevText,
55+
prev3Text: state.prev2Text,
5456
};
5557
});
5658
},
@@ -73,12 +75,16 @@ var TextEventsExample = React.createClass({
7375
onSubmitEditing={(event) => this.updateText(
7476
'onSubmitEditing text: ' + event.nativeEvent.text
7577
)}
78+
onKeyPress={(event) => {
79+
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
80+
}}
7681
style={styles.default}
7782
/>
7883
<Text style={styles.eventLabel}>
7984
{this.state.curText}{'\n'}
8085
(prev: {this.state.prevText}){'\n'}
81-
(prev2: {this.state.prev2Text})
86+
(prev2: {this.state.prev2Text}){'\n'}
87+
(prev3: {this.state.prev3Text})
8288
</Text>
8389
</View>
8490
);

Libraries/Components/TextInput/TextInput.js

+7
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ var TextInput = React.createClass({
217217
* Callback that is called when the text input's submit button is pressed.
218218
*/
219219
onSubmitEditing: PropTypes.func,
220+
/**
221+
* Callback that is called when a key is pressed.
222+
* Pressed key value is passed as an argument to the callback handler.
223+
* Fires before onChange callbacks.
224+
* @platform ios
225+
*/
226+
onKeyPress: PropTypes.func,
220227
/**
221228
* Invoked on mount and layout changes with `{x, y, width, height}`.
222229
*/

Libraries/Text/RCTTextField.h

+3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
@property (nonatomic, strong) UIColor *placeholderTextColor;
2121
@property (nonatomic, assign) NSInteger mostRecentEventCount;
2222
@property (nonatomic, strong) NSNumber *maxLength;
23+
@property (nonatomic, assign) BOOL textWasPasted;
2324

2425
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
26+
2527
- (void)textFieldDidChange;
28+
- (void)sendKeyValueForString:(NSString *)string;
2629

2730
@end

Libraries/Text/RCTTextField.m

+22
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,23 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
3939
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
4040
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
4141

42+
- (void)sendKeyValueForString:(NSString *)string
43+
{
44+
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
45+
reactTag:self.reactTag
46+
text:nil
47+
key:string
48+
eventCount:_nativeEventCount];
49+
}
50+
51+
// This method is overriden for `onKeyPress`. The manager
52+
// will not send a keyPress for text that was pasted.
53+
- (void)paste:(id)sender
54+
{
55+
_textWasPasted = YES;
56+
[super paste:sender];
57+
}
58+
4259
- (void)setText:(NSString *)text
4360
{
4461
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
@@ -134,6 +151,7 @@ - (void)textFieldDidChange
134151
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
135152
reactTag:self.reactTag
136153
text:self.text
154+
key:nil
137155
eventCount:_nativeEventCount];
138156
}
139157

@@ -142,13 +160,15 @@ - (void)textFieldEndEditing
142160
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
143161
reactTag:self.reactTag
144162
text:self.text
163+
key:nil
145164
eventCount:_nativeEventCount];
146165
}
147166
- (void)textFieldSubmitEditing
148167
{
149168
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
150169
reactTag:self.reactTag
151170
text:self.text
171+
key:nil
152172
eventCount:_nativeEventCount];
153173
}
154174

@@ -162,6 +182,7 @@ - (void)textFieldBeginEditing
162182
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
163183
reactTag:self.reactTag
164184
text:self.text
185+
key:nil
165186
eventCount:_nativeEventCount];
166187
}
167188

@@ -181,6 +202,7 @@ - (BOOL)resignFirstResponder
181202
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
182203
reactTag:self.reactTag
183204
text:self.text
205+
key:nil
184206
eventCount:_nativeEventCount];
185207
}
186208
return result;

Libraries/Text/RCTTextFieldManager.m

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ - (UIView *)view
3131

3232
- (BOOL)textField:(RCTTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
3333
{
34+
// Only allow single keypresses for onKeyPress, pasted text will not be sent.
35+
if (textField.textWasPasted) {
36+
textField.textWasPasted = NO;
37+
} else {
38+
[textField sendKeyValueForString:string];
39+
}
40+
3441
if (textField.maxLength == nil || [string isEqualToString:@"\n"]) { // Make sure forms can be submitted via return
3542
return YES;
3643
}
@@ -54,6 +61,14 @@ - (BOOL)textField:(RCTTextField *)textField shouldChangeCharactersInRange:(NSRan
5461
}
5562
}
5663

64+
// This method allows us to detect a `Backspace` keyPress
65+
// even when there is no more text in the TextField
66+
- (BOOL)keyboardInputShouldDelete:(RCTTextField *)textField
67+
{
68+
[self textField:textField shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:@""];
69+
return YES;
70+
}
71+
5772
RCT_EXPORT_VIEW_PROPERTY(caretHidden, BOOL)
5873
RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL)
5974
RCT_REMAP_VIEW_PROPERTY(editable, enabled, BOOL)

Libraries/Text/RCTTextView.m

+35-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@
1414
#import "RCTUtils.h"
1515
#import "UIView+React.h"
1616

17+
@interface RCTUITextView : UITextView
18+
19+
@property (nonatomic, assign) BOOL textWasPasted;
20+
21+
@end
22+
23+
@implementation RCTUITextView
24+
25+
- (void)paste:(id)sender
26+
{
27+
_textWasPasted = YES;
28+
[super paste:sender];
29+
}
30+
31+
@end
32+
1733
@implementation RCTTextView
1834
{
1935
RCTEventDispatcher *_eventDispatcher;
@@ -33,7 +49,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
3349
_eventDispatcher = eventDispatcher;
3450
_placeholderTextColor = [self defaultPlaceholderTextColor];
3551

36-
_textView = [[UITextView alloc] initWithFrame:self.bounds];
52+
_textView = [[RCTUITextView alloc] initWithFrame:self.bounds];
3753
_textView.backgroundColor = [UIColor clearColor];
3854
_textView.scrollsToTop = NO;
3955
_textView.delegate = self;
@@ -56,15 +72,15 @@ - (void)updateFrames
5672
// first focused.
5773
UIEdgeInsets adjustedFrameInset = UIEdgeInsetsZero;
5874
adjustedFrameInset.left = _contentInset.left - 5;
59-
75+
6076
UIEdgeInsets adjustedTextContainerInset = _contentInset;
6177
adjustedTextContainerInset.top += 5;
6278
adjustedTextContainerInset.left = 0;
63-
79+
6480
CGRect frame = UIEdgeInsetsInsetRect(self.bounds, adjustedFrameInset);
6581
_textView.frame = frame;
6682
_placeholderView.frame = frame;
67-
83+
6884
_textView.textContainerInset = adjustedTextContainerInset;
6985
_placeholderView.textContainerInset = adjustedTextContainerInset;
7086
}
@@ -138,8 +154,18 @@ - (NSString *)text
138154
return _textView.text;
139155
}
140156

141-
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
157+
- (BOOL)textView:(RCTUITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
142158
{
159+
if (textView.textWasPasted) {
160+
textView.textWasPasted = NO;
161+
} else {
162+
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
163+
reactTag:self.reactTag
164+
text:nil
165+
key:text
166+
eventCount:_nativeEventCount];
167+
}
168+
143169
if (_maxLength == nil) {
144170
return YES;
145171
}
@@ -215,6 +241,7 @@ - (void)textViewDidBeginEditing:(UITextView *)textView
215241
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
216242
reactTag:self.reactTag
217243
text:textView.text
244+
key:nil
218245
eventCount:_nativeEventCount];
219246
}
220247

@@ -225,6 +252,7 @@ - (void)textViewDidChange:(UITextView *)textView
225252
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
226253
reactTag:self.reactTag
227254
text:textView.text
255+
key:nil
228256
eventCount:_nativeEventCount];
229257

230258
}
@@ -234,6 +262,7 @@ - (void)textViewDidEndEditing:(UITextView *)textView
234262
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
235263
reactTag:self.reactTag
236264
text:textView.text
265+
key:nil
237266
eventCount:_nativeEventCount];
238267
}
239268

@@ -253,6 +282,7 @@ - (BOOL)resignFirstResponder
253282
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
254283
reactTag:self.reactTag
255284
text:_textView.text
285+
key:nil
256286
eventCount:_nativeEventCount];
257287
}
258288
return result;

React/Base/RCTEventDispatcher.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ typedef NS_ENUM(NSInteger, RCTTextEventType) {
1616
RCTTextEventTypeBlur,
1717
RCTTextEventTypeChange,
1818
RCTTextEventTypeSubmit,
19-
RCTTextEventTypeEnd
19+
RCTTextEventTypeEnd,
20+
RCTTextEventTypeKeyPress
2021
};
2122

2223
typedef NS_ENUM(NSInteger, RCTScrollEventType) {
@@ -95,6 +96,7 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName);
9596
- (void)sendTextEventWithType:(RCTTextEventType)type
9697
reactTag:(NSNumber *)reactTag
9798
text:(NSString *)text
99+
key:(NSString *)key
98100
eventCount:(NSInteger)eventCount;
99101

100102
/**

React/Base/RCTEventDispatcher.m

+26-5
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ - (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body
144144
- (void)sendTextEventWithType:(RCTTextEventType)type
145145
reactTag:(NSNumber *)reactTag
146146
text:(NSString *)text
147+
key:(NSString *)key
147148
eventCount:(NSInteger)eventCount
148149
{
149150
static NSString *events[] = {
@@ -152,16 +153,36 @@ - (void)sendTextEventWithType:(RCTTextEventType)type
152153
@"change",
153154
@"submitEditing",
154155
@"endEditing",
156+
@"keyPress"
155157
};
156158

157-
[self sendInputEventWithName:events[type] body:text ? @{
158-
@"text": text,
159-
@"eventCount": @(eventCount),
160-
@"target": reactTag
161-
} : @{
159+
NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
162160
@"eventCount": @(eventCount),
163161
@"target": reactTag
164162
}];
163+
164+
if (text) {
165+
body[@"text"] = text;
166+
}
167+
168+
if (key) {
169+
if (key.length == 0) {
170+
key = @"Backspace"; // backspace
171+
} else {
172+
switch ([key characterAtIndex:0]) {
173+
case '\t':
174+
key = @"Tab";
175+
break;
176+
case '\n':
177+
key = @"Enter";
178+
default:
179+
break;
180+
}
181+
}
182+
body[@"key"] = key;
183+
}
184+
185+
[self sendInputEventWithName:events[type] body:body];
165186
}
166187

167188
- (void)sendEvent:(id<RCTEvent>)event

React/Views/RCTViewManager.m

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ - (NSArray *)customBubblingEventTypes
8080
@"blur",
8181
@"submitEditing",
8282
@"endEditing",
83+
@"keyPress",
8384

8485
// Touch events
8586
@"touchStart",

0 commit comments

Comments
 (0)