Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Blur & Focus] Support relinquishing focus from multiple views via container #113

Closed
ide opened this issue Mar 1, 2015 · 31 comments
Closed
Labels
Good first issue Interested in collaborating? Take a stab at fixing one of these issues. Resolution: Locked This issue was locked by the bot.

Comments

@ide
Copy link
Contributor

ide commented Mar 1, 2015

If you have two text inputs in a container view, calling container.blur() could make both of the text inputs try to resign their first responder status. In UIKit this is built-in as -[UIView endEditing:animated]. This way you can just blur the root view if you want the keyboard to go away.

@vjeux
Copy link
Contributor

vjeux commented Mar 1, 2015

I'm not sure I understand. You can do .blur() on Text ref right now. Do you want to do blur() on an arbitrary View?

@ide
Copy link
Contributor Author

ide commented Mar 1, 2015

Yeah, it blurs any descendant that has focus. The descendant could be a TextInput but could also be any other kind of input element that can become the first responder.

@vjeux
Copy link
Contributor

vjeux commented Mar 1, 2015

You can actually call blur() on a View today. See NativeMethodMixin::blur. Can you tell me if it's doing what you want?

@brentvatne
Copy link
Collaborator

ping @ide

@ide
Copy link
Contributor Author

ide commented May 6, 2015

Confirmed that this.refs.parentView.blur does not blur its child views. Where this would be handy is in the navigator stack where a scene will lose focus without being unmounted, and you want to make sure to blur all of its text fields. So instead of

this.refs.textInput1.blur();
this.refs.textInput2.blur();
this.refs.textInput3.blur();

you want to write

this.refs.container.blur();

@brentvatne
Copy link
Collaborator

This does seem like it would be useful! Another issue came up (referenced above) where this would solve the problem. cc @ide @vjeux

@dhrrgn
Copy link
Contributor

dhrrgn commented May 20, 2015

@brentvatne I came up with a simple solution for this internally where I wrote a component that essentially just extends UIView and blurs all of the subviews on touch. Here is the guts of it:

@implementation KBCloserView

- (void)didMoveToSuperview
{
    self.userInteractionEnabled = TRUE;
    [super didMoveToSuperview];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    for (UIView *subview in self.subviews) {
        [subview endEditing:YES];
    }
}

@end

So I just convert my container View's to <KBCloserView> components, and 💥 tap anywhere in the view to close the keyboard.

I share this because I think adding a closeKeyboardOnPress type property to the RN View component, which just does the above, would be simple and solve this problem entirely without having to go through the effort of changing how blur works (I personally like that it only blurs the specific input).

@brentvatne
Copy link
Collaborator

Nice on @dhrrgn! Seems simple enough. Might be worth putting into a library on npm until a solution is integrated into core

@brentvatne brentvatne changed the title Support relinquishing focus from views [Blur & Focus] Support relinquishing focus from views May 31, 2015
@brentvatne brentvatne changed the title [Blur & Focus] Support relinquishing focus from views [Blur & Focus] Support relinquishing focus from multiple views via container May 31, 2015
@dhrrgn
Copy link
Contributor

dhrrgn commented May 31, 2015

@brentvatne I will do that tomorrow...right after I figure out how to create an Xcode project people can just add to their's (bit of an Xcode "noob") haha. (Side note: it would be awesome if their was a "Creating Distributable Native Components" section of the docs for this sort of thing).

@brentvatne
Copy link
Collaborator

@dhrrgn - I have something about that on my blog: Packaging a React Native component, I hesitated to add it to the docs because it seemed like a temporary solution, but we haven't worked out a better one yet (aside from react-native new-library from within a React Native project, in order to create a skeleton bridge module component - you could do this and just change it up accordingly rather than create a new project from scratch as I mention in my blog post)

@dhrrgn
Copy link
Contributor

dhrrgn commented May 31, 2015

@brentvatne Awesome, thanks.

@grabbou
Copy link
Contributor

grabbou commented Jun 8, 2015

Any updates on that? Calling blur on a view should act like endEditing as per @ide suggestion. If not, maybe worth adding another method.

My use case:
Using @brentvatne modal with click backdrop to dismiss feature. When the keyboard is opened, I'd prefer closing the keyboard instead of dismissing the modal so user has the ability to continue editing. Not all keyboard types are provided done button as of now (e.g. numpad) so it gets tricky.

@brentvatne
Copy link
Collaborator

@grabbou - do you have time to make the changes that @ide mentioned above and submit a PR?

@grabbou
Copy link
Contributor

grabbou commented Jun 8, 2015

Yeah, can do. Just need to dig into the source code as I haven't contributed yet but would love to.

Another thing worth noticing is that the method signature is actually endEditing:force instead of mentioned :animated.

After doing a couple of more tests it looks like the this.refs.mainView.blur() blurs all the child TextViews (including TextInputs that are inside another view).

Sample structure returned from render:

<View ref='modal'>
    <View style={styles.container}>
          <TextInput .... />
          <TextInput .... />
    </View>
</View>

and the invocation:

this.refs.modal.blur();

@brentvatne
Copy link
Collaborator

@ide - what do you think about @grabbou's observation above?

@ide
Copy link
Contributor Author

ide commented Jun 15, 2015

Not sure why that's happening. Maybe something changed. Feel free to close this if it just works now.

@brentvatne
Copy link
Collaborator

@ide - seems to be a false alarm, still not working for me with 0.5: https://rnplay.org/apps/SwEnTw - tried it against master as well, same thing: https://rnplay.org/apps/AxZnJw

@grabbou - can you reproduce that fix on rnplay.org?

Still an outstanding issue as far as I can tell

@admmasters
Copy link
Contributor

Not working for me either - an outstanding issue I'm afraid.

@brentvatne
Copy link
Collaborator

Anyone up for fixing this? Would be a great way to learn about some of the React Native internals

@brentvatne brentvatne added the Good first issue Interested in collaborating? Take a stab at fixing one of these issues. label Jun 23, 2015
@josebalius
Copy link

It is still outstanding, I made another rnplay: https://rnplay.org/apps/aAb8aA/ to showcase the problem with 0.6.0 and can reproduce with 0.8.0-rc, I'm going to give this a try with my limited Objective-C skills.

@brentvatne
Copy link
Collaborator

It looks like nobody on the team at Facebook has a need for this at the moment so I've tagged it as Community Responsibility 😄

@josebalius
Copy link

@brentvatne So i went off and tried to do it, and I have something working but wanted to show you first as this is my first time messing with the internals so want to see if I should change my approach.

In NativeMethodsMixin.js I added the following under blur():

endEditing: function() {
    RCTUIManager.endEditing(findNodeHandle(this));
  }

In RCTUIManager.m I did the following:

- (void)endEditingForShadowView:(NSNumber *)reactTag manager:(RCTUIManager *)uiManager viewRegistry:(RCTSparseArray *)viewRegistry
{
    RCTShadowView *shadowView = uiManager.shadowViewRegistry[reactTag];

  for(RCTShadowView *subview in [shadowView reactSubviews]) {
      if([[subview reactSubviews] count] > 0) {
          [uiManager endEditingForShadowView:subview.reactTag manager:uiManager viewRegistry:viewRegistry];
      } else {
          UIView *view = viewRegistry[subview.reactTag];
          [view resignFirstResponder];
      }
  }
}

RCT_EXPORT_METHOD(endEditing:(NSNumber *)reactTag)
{
  if (!reactTag) return;
  [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
      [uiManager endEditingForShadowView:reactTag manager:uiManager viewRegistry:viewRegistry];
  }];
}

My test case:

blurFields() {
        this.refs.xview.endEditing();
}

<View style={{flex: 1}} ref="xview">
                <ScrollView ref="scrollView">
                    <View testProp={true}>
                        <TextInput ref="field1" style={{backgroundColor: 'red', width: 100, height: 20}} />
                        <TextInput ref="field2" style={{backgroundColor: 'blue', width: 100, height: 20, marginTop: 20}} />
                        <View>
                            <TextInput ref="field3" style={{backgroundColor: 'green', width: 100, height: 20, marginTop: 20}} />
                        </View>
                        <TouchableHighlight onPress={() => this.blurFields()}>
                            <Text>Blur fields</Text>
                        </TouchableHighlight>
                    </View>

                </ScrollView>
            </View>

Everything seems to work okay and the functionality works as expected. What do you think? I'll submit a proper PR if you think it's good.

@brentvatne
Copy link
Collaborator

@josebalius - this looks very reasonable to me, @tadeuzagallo what do you think?

One comment about the API: it would be nice if this was just a modification of blur so that blur would affect all children as well.

@josebalius
Copy link

@brentvatne roger on the API, i thought about the same thing shouldn't be a problem. If @tadeuzagallo is fine with the code i'll submit the PR with an API like this.refs.parentView.blur(true) true for deep search, if not true then it will act the same as it does now which is tries to blur the active text field.

Actually maybe we don't need the extra param at all, if it's on a text field that you are calling it on the loop should have to search once so I think that works as well.....we can just replace the blur functionality altogether.

@brentvatne
Copy link
Collaborator

@josebalius - yeah I agree, if it's just a TextField then we don't have to be concerned about traversing any children

@wluxion
Copy link

wluxion commented Sep 18, 2015

This can be closed, no?
https://github.com/facebook/react-native/blob/master/Libraries/Utilities/dismissKeyboard.js

var dismissKeyboard = require('dismissKeyboard')
...
onClose: function() {
    dismissKeyboard();
}

@ryanmcdermott
Copy link

@wluxion Looks interesting, how do you require the dismissKeyboard Utility in a project? I didn't see Utilities or that particular one in the docs. Will it work on iOS and Android?

@brentvatne
Copy link
Collaborator

We are exposing TextInputState in #3308 which will allow you to do this too. I'm not sure it's worth exposing this one line function as it will increase API surface area, if you believe it's important then feel free to chime in on that PR! Thanks @wluxion for the heads-up.

@ryanmcdermott
Copy link

@brentvatne Looks great, I look forward to it.

@josebalius
Copy link

@brentvatne Any update on this? I am upgrading a couple of projects to the latest RN and running into blur issues not hiding the keyboard, do we have docs on the proper way to do it?

@brentvatne
Copy link
Collaborator

@josebalius - hmmm I'm not sure about this, could you put together a small example and create a new issue?

@facebook facebook locked as resolved and limited conversation to collaborators Jul 23, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 23, 2018
react-one pushed a commit to react-one/react-native that referenced this issue Sep 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Good first issue Interested in collaborating? Take a stab at fixing one of these issues. Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests

10 participants