From 2b1795c5add2e1442878a884d6dcd8f0ec5c1572 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 27 Jun 2017 16:05:11 -0700 Subject: [PATCH] Support `` on iOS Summary: Standard only-numeric (number pad) keyboard on iOS does not have any "Done" or "Enter" button, and this is often very badly hurt user experience. Usually it can be solved by implementing custom `inputAccessoryView`, but RN does not have built-in support for customizing it. So, this commit introduced limited support only for "Done" button (returnKeyType="done") and it should suite very well for the vast majority of use cases. This is highly requested feature, see more details here: https://github.com/facebook/react-native/issues/1190 Reviewed By: mmmulani Differential Revision: D5268020 fbshipit-source-id: 90bd5bffac6aaa1fb7c5c2ac539b35b04d45918f --- .../Text/RCTBackedTextInputViewProtocol.h | 1 + Libraries/Text/RCTTextInput.m | 60 +++++++++++++++++++ RNTester/js/TextInputExample.ios.js | 1 + 3 files changed, 62 insertions(+) diff --git a/Libraries/Text/RCTBackedTextInputViewProtocol.h b/Libraries/Text/RCTBackedTextInputViewProtocol.h index a49f1e6c9..17b39486d 100644 --- a/Libraries/Text/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/RCTBackedTextInputViewProtocol.h @@ -18,5 +18,6 @@ @property (nonatomic, assign, readonly) BOOL textWasPasted; @property (nonatomic, strong, nullable) UIFont *font; @property (nonatomic, assign) UIEdgeInsets textContainerInset; +@property (nonatomic, strong, nullable) UIView *inputAccessoryView; @end diff --git a/Libraries/Text/RCTTextInput.m b/Libraries/Text/RCTTextInput.m index 21f8b8cff..86591a149 100644 --- a/Libraries/Text/RCTTextInput.m +++ b/Libraries/Text/RCTTextInput.m @@ -155,4 +155,64 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) [self.backedTextInputView reactFocusIfNeeded]; } +#pragma mark - Custom Input Accessory View + +- (void)didSetProps:(NSArray *)changedProps +{ + [self invalidateInputAccessoryView]; +} + +- (void)invalidateInputAccessoryView +{ +#if !TARGET_OS_TV + UIView *textInputView = self.backedTextInputView; + UIKeyboardType keyboardType = textInputView.keyboardType; + + // These keyboard types (all are number pads) don't have a "Done" button by default, + // so we create an `inputAccessoryView` with this button for them. + BOOL shouldHaveInputAccesoryView = + ( + keyboardType == UIKeyboardTypeNumberPad || + keyboardType == UIKeyboardTypePhonePad || + keyboardType == UIKeyboardTypeDecimalPad || + keyboardType == UIKeyboardTypeASCIICapableNumberPad + ) && + textInputView.returnKeyType == UIReturnKeyDone; + + BOOL hasInputAccesoryView = textInputView.inputAccessoryView != nil; + + if (hasInputAccesoryView == shouldHaveInputAccesoryView) { + return; + } + + if (shouldHaveInputAccesoryView) { + UIToolbar *toolbarView = [[UIToolbar alloc] init]; + [toolbarView sizeToFit]; + UIBarButtonItem *flexibleSpace = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil + action:nil]; + UIBarButtonItem *doneButton = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone + target:self + action:@selector(handleInputAccessoryDoneButton)]; + toolbarView.items = @[flexibleSpace, doneButton]; + textInputView.inputAccessoryView = toolbarView; + } + else { + textInputView.inputAccessoryView = nil; + } + + // We have to call `reloadInputViews` for focused text inputs to update an accessory view. + if (textInputView.isFirstResponder) { + [textInputView reloadInputViews]; + } +#endif +} + +- (void)handleInputAccessoryDoneButton +{ + [self.backedTextInputView endEditing:YES]; +} + @end diff --git a/RNTester/js/TextInputExample.ios.js b/RNTester/js/TextInputExample.ios.js index 59fd36eb6..1e0c24e72 100644 --- a/RNTester/js/TextInputExample.ios.js +++ b/RNTester/js/TextInputExample.ios.js @@ -239,6 +239,7 @@ class BlurOnSubmitExample extends React.Component { ref="4" style={styles.default} keyboardType="numeric" + returnKeyType="done" placeholder="blurOnSubmit = false" blurOnSubmit={false} onSubmitEditing={() => this.focusNextField('5')}