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')}