Skip to content

Commit

Permalink
Support <TextInput keyboardType="numeric" returnKeyType="done" /> o…
Browse files Browse the repository at this point in the history
…n 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:
#1190

Reviewed By: mmmulani

Differential Revision: D5268020

fbshipit-source-id: 90bd5bffac6aaa1fb7c5c2ac539b35b04d45918f
  • Loading branch information
shergin authored and facebook-github-bot committed Jun 27, 2017
1 parent 1081b21 commit 2b1795c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 0 deletions.
1 change: 1 addition & 0 deletions Libraries/Text/RCTBackedTextInputViewProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
60 changes: 60 additions & 0 deletions Libraries/Text/RCTTextInput.m
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,64 @@ - (void)didMoveToWindow
[self.backedTextInputView reactFocusIfNeeded];
}

#pragma mark - Custom Input Accessory View

- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
[self invalidateInputAccessoryView];
}

- (void)invalidateInputAccessoryView
{
#if !TARGET_OS_TV
UIView<RCTBackedTextInputViewProtocol> *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;

This comment has been minimized.

Copy link
@dariocravero

dariocravero Jul 21, 2017

should this support go, next, search and send too?

This comment has been minimized.

Copy link
@shergin

shergin Jul 22, 2017

Author Contributor

Goooooood question. Do you need this functionality? The main problem here is i18n; UIBarButtonSystemItemDone automatically provides us "Done" button.

This comment has been minimized.

Copy link
@dariocravero

dariocravero Jul 22, 2017

Hi @shergin, thanks for asking. Yes, we do, for next.
I understand where you're coming from though, I was reading on https://developer.apple.com/documentation/uikit/uibarbuttonsystemitem?language=objc and can't seem to find one that would suit neither next nor go. There's one for search though.

I wonder, is there a way to access those i18n system values? After all, next and go are being translated in the keyboard already. The other solutions that I can see are either keeping some internal i18n records for this or asking the user to provide that text.

What're your thoughts on that?

This comment has been minimized.

Copy link
@shergin

shergin Aug 2, 2017

Author Contributor

Yeah. AFAIK, there is no way to access i18n values inside the system keyboard. We can easily wrap these strings into NSLocalizedStrings which makes them localizable using standard iOS approach. Probably this is good idea.

And moreover, if we do this, we will have to introduce a way to opt-out this feature (because current done exclusivity also works as opt-in trigger).

If you (or someone else) have strong feeling that we need this fuctionality... I would love to review your PR! 😄


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
1 change: 1 addition & 0 deletions RNTester/js/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class BlurOnSubmitExample extends React.Component {
ref="4"
style={styles.default}
keyboardType="numeric"
returnKeyType="done"

This comment has been minimized.

Copy link
@MikeFvv

MikeFvv Oct 27, 2017

thank you very much , I need this

This comment has been minimized.

Copy link
@shergin

shergin Oct 27, 2017

Author Contributor

❤️

placeholder="blurOnSubmit = false"
blurOnSubmit={false}
onSubmitEditing={() => this.focusNextField('5')}
Expand Down

8 comments on commit 2b1795c

@fabriziogiordano
Copy link

@fabriziogiordano fabriziogiordano commented on 2b1795c Jun 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add this new feature in the official documentation.

@sidrees00
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work on react-native version 0.45.1? I don't have the referenced files in my node_modules/react-native folder.

@leandrojo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very well! Thanks!!!

@douglasjunior
Copy link

@douglasjunior douglasjunior commented on 2b1795c Sep 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit brought a problem when use a custom ToolBar, like IQKeyboardManager.

Is there any way to disable this feature?

@shergin
Copy link
Contributor Author

@shergin shergin commented on 2b1795c Sep 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@douglasjunior Can you change returnKeyType for your <TextInput>s?

@shergin
Copy link
Contributor Author

@shergin shergin commented on 2b1795c Sep 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@douglasjunior Oh, I see what you mean. RN uses self.textInputAccessoryView == nil as a flag to know should we rebuild/reset textInputAccessoryView or not, but that library also modifies this.
Technically, it can be fixed by having additional variable which will store the fact that textInputAccessoryView was constructed by RN.
I would love to see PR. 😄

@douglasjunior
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shergin thanks. I'll try it.

But I think that if a textInputAccessoryView already exists, the RN should not replace it. What do you think?

@douglasjunior
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #16071

Please sign in to comment.