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:
Dave Sibiski
2015-11-02 09:13:41 -08:00
committed by facebook-github-bot-6
parent 6539b26810
commit 6c7c845145
9 changed files with 119 additions and 12 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;