diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm index b0d71dcd3508bb..511cb610b8d9d2 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm @@ -22,6 +22,7 @@ /** Native iOS text field bottom keyboard offset amount */ static const CGFloat kSingleLineKeyboardBottomOffset = 15.0; +static NSSet *returnKeyTypesSet; @implementation RCTBaseTextInputView { __weak RCTBridge *_bridge; @@ -62,6 +63,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge if (self = [super initWithFrame:CGRectZero]) { _bridge = bridge; _eventDispatcher = bridge.eventDispatcher; + [self initializeReturnKeyType]; } return self; @@ -622,6 +624,7 @@ - (void)didMoveToWindow { if (self.autoFocus && !_didMoveToWindow) { [self.backedTextInputView reactFocus]; + [self initializeReturnKeyType]; } else { [self.backedTextInputView reactFocusIfNeeded]; } @@ -658,17 +661,68 @@ - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID }]; } +- (NSString *)returnKeyTypeToString:(UIReturnKeyType)returnKeyType { +switch (returnKeyType) { + case UIReturnKeyDefault: + return @"Default"; + case UIReturnKeyGo: + return @"Go"; + case UIReturnKeyNext: + return @"Next"; + case UIReturnKeySearch: + return @"Search"; + case UIReturnKeySend: + return @"Send"; + case UIReturnKeyYahoo: + return @"Yahoo"; + case UIReturnKeyGoogle: + return @"Google"; + case UIReturnKeyRoute: + return @"Route"; + case UIReturnKeyJoin: + return @"Join"; + case UIReturnKeyEmergencyCall: + return @"Emergency Call"; + default: + return @"Done"; + } + } + +- (void)initializeReturnKeyType { + returnKeyTypesSet = [NSSet setWithObjects: + @(UIReturnKeyDone), + @(UIReturnKeyGo), + @(UIReturnKeyDefault), + @(UIReturnKeyNext), + @(UIReturnKeySearch), + @(UIReturnKeySend), + @(UIReturnKeyYahoo), + @(UIReturnKeyGoogle), + @(UIReturnKeyRoute), + @(UIReturnKeyJoin), + @(UIReturnKeyRoute), + @(UIReturnKeyEmergencyCall), + nil + ]; +} + - (void)setDefaultInputAccessoryView { UIView *textInputView = self.backedTextInputView; UIKeyboardType keyboardType = textInputView.keyboardType; - // These keyboard types (all are number pads) don't have a "Done" button by default, + // These keyboard types (all are number pads) don't have a Return Key button by default, // so we create an `inputAccessoryView` with this button for them. + + + UIReturnKeyType returnKeyType = textInputView.returnKeyType; + + BOOL containsKeyType = [returnKeyTypesSet containsObject:@(returnKeyType)]; + BOOL shouldHaveInputAccessoryView = (keyboardType == UIKeyboardTypeNumberPad || keyboardType == UIKeyboardTypePhonePad || keyboardType == UIKeyboardTypeDecimalPad || keyboardType == UIKeyboardTypeASCIICapableNumberPad) && - textInputView.returnKeyType == UIReturnKeyDone; + containsKeyType; if (_hasInputAccessoryView == shouldHaveInputAccessoryView) { return; @@ -677,14 +731,17 @@ - (void)setDefaultInputAccessoryView _hasInputAccessoryView = shouldHaveInputAccessoryView; if (shouldHaveInputAccessoryView) { + NSString *buttonLabel = [self returnKeyTypeToString:returnKeyType]; + UIToolbar *toolbarView = [UIToolbar new]; [toolbarView sizeToFit]; UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - UIBarButtonItem *doneButton = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone - target:self - action:@selector(handleInputAccessoryDoneButton)]; + UIBarButtonItem *doneButton = + [[UIBarButtonItem alloc] initWithTitle:buttonLabel + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleInputAccessoryDoneButton)]; toolbarView.items = @[ flexibleSpace, doneButton ]; textInputView.inputAccessoryView = toolbarView; } else { diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 2216d0e96b2dbd..b27b5e9a8b48db 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -27,6 +27,8 @@ @interface RCTTextInputComponentView () @end +static NSSet *returnKeyTypesSet; + @implementation RCTTextInputComponentView { TextInputShadowNode::ConcreteState::Shared _state; UIView *_backedTextInputView; @@ -62,32 +64,39 @@ @implementation RCTTextInputComponentView { - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { - const auto &defaultProps = TextInputShadowNode::defaultSharedProps(); - _props = defaultProps; + const auto &defaultProps = TextInputShadowNode::defaultSharedProps(); + _props = defaultProps; - _backedTextInputView = defaultProps->traits.multiline ? [RCTUITextView new] : [RCTUITextField new]; - _backedTextInputView.textInputDelegate = self; - _ignoreNextTextInputCall = NO; - _comingFromJS = NO; - _didMoveToWindow = NO; - [self addSubview:_backedTextInputView]; - } + _backedTextInputView = defaultProps->traits.multiline ? [RCTUITextView new] : [RCTUITextField new]; + _backedTextInputView.textInputDelegate = self; + _ignoreNextTextInputCall = NO; + _comingFromJS = NO; + _didMoveToWindow = NO; + + [self addSubview:_backedTextInputView]; + [self initializeReturnKeyType]; + - return self; + } + + return self; } - (void)didMoveToWindow { [super didMoveToWindow]; - if (self.window && !_didMoveToWindow) { - const auto &props = static_cast(*_props); - if (props.autoFocus) { - [_backedTextInputView becomeFirstResponder]; + if (self.window && !_didMoveToWindow) { + const auto &props = static_cast(*_props); + if (props.autoFocus) { + [_backedTextInputView becomeFirstResponder]; + } + _didMoveToWindow = YES; + [self initializeReturnKeyType]; + } - _didMoveToWindow = YES; - } - [self _restoreTextSelection]; + + [self _restoreTextSelection]; } #pragma mark - RCTViewComponentView overrides @@ -386,7 +395,7 @@ - (void)textInputDidChange if (_eventEmitter) { const auto &textInputEventEmitter = static_cast(*_eventEmitter); - textInputEventEmitter.onChange([self _textInputMetrics]); + textInputEventEmitter.onChange([self _textInputMetrics]); } } @@ -462,6 +471,51 @@ - (void)setTextAndSelection:(NSInteger)eventCount #pragma mark - Default input accessory view +- (NSString *)returnKeyTypeToString:(UIReturnKeyType)returnKeyType { +switch (returnKeyType) { + case UIReturnKeyDefault: + return @"Default"; + case UIReturnKeyGo: + return @"Go"; + case UIReturnKeyNext: + return @"Next"; + case UIReturnKeySearch: + return @"Search"; + case UIReturnKeySend: + return @"Send"; + case UIReturnKeyYahoo: + return @"Yahoo"; + case UIReturnKeyGoogle: + return @"Google"; + case UIReturnKeyRoute: + return @"Route"; + case UIReturnKeyJoin: + return @"Join"; + case UIReturnKeyEmergencyCall: + return @"Emergency Call"; + default: + return @"Done"; + } + } + +- (void)initializeReturnKeyType { + returnKeyTypesSet = [NSSet setWithObjects: + @(UIReturnKeyDone), + @(UIReturnKeyGo), + @(UIReturnKeyDefault), + @(UIReturnKeyNext), + @(UIReturnKeySearch), + @(UIReturnKeySend), + @(UIReturnKeyYahoo), + @(UIReturnKeyGoogle), + @(UIReturnKeyRoute), + @(UIReturnKeyJoin), + @(UIReturnKeyRoute), + @(UIReturnKeyEmergencyCall), + nil + ]; +} + - (void)setDefaultInputAccessoryView { // InputAccessoryView component sets the inputAccessoryView when inputAccessoryViewID exists @@ -473,27 +527,35 @@ - (void)setDefaultInputAccessoryView } UIKeyboardType keyboardType = _backedTextInputView.keyboardType; + UIReturnKeyType returnKeyType = _backedTextInputView.returnKeyType; - // These keyboard types (all are number pads) don't have a "Done" button by default, + BOOL containsKeyType = [returnKeyTypesSet containsObject:@(returnKeyType)]; + + + + // These keyboard types (all are number pads) don't have a "returnKey" button by default, // so we create an `inputAccessoryView` with this button for them. BOOL shouldHaveInputAccessoryView = (keyboardType == UIKeyboardTypeNumberPad || keyboardType == UIKeyboardTypePhonePad || keyboardType == UIKeyboardTypeDecimalPad || keyboardType == UIKeyboardTypeASCIICapableNumberPad) && - _backedTextInputView.returnKeyType == UIReturnKeyDone; + containsKeyType; if ((_backedTextInputView.inputAccessoryView != nil) == shouldHaveInputAccessoryView) { return; } if (shouldHaveInputAccessoryView) { + NSString *buttonLabel = [self returnKeyTypeToString:returnKeyType]; + UIToolbar *toolbarView = [UIToolbar new]; [toolbarView sizeToFit]; UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - UIBarButtonItem *doneButton = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone - target:self - action:@selector(handleInputAccessoryDoneButton)]; + UIBarButtonItem *doneButton = + [[UIBarButtonItem alloc] initWithTitle:buttonLabel + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleInputAccessoryDoneButton)]; toolbarView.items = @[ flexibleSpace, doneButton ]; _backedTextInputView.inputAccessoryView = toolbarView; } else {