By McG


2015-03-28 03:12:49 8 Comments

I've seen this hack for native apps to auto scroll the window, but wondering best way to do it for react-native... When a field gets focus and is positioned low in the view, the keyboard will cover up the text field. You can see this issue in example UIExplorer's TextInputExample.js view. Does anyone have a good solution?

14 comments

@Nath 2016-06-23 19:39:26

You can combine a few of the methods into something a little simpler.

Attach a onFocus listener on your inputs

<TextInput ref="password" secureTextEntry={true} 
           onFocus={this.scrolldown.bind(this,'password')}
/>

Our scroll down method looks something like :

scrolldown(ref) {
    const self = this;
    this.refs[ref].measure((ox, oy, width, height, px, py) => {
        self.refs.scrollView.scrollTo({y: oy - 200});
    });
}

This tells our scroll view (remember to add a ref) to scroll to down to the position of our focused input - 200 (it's roughly the size of the keyboard)

componentWillMount() {
    this.keyboardDidHideListener = Keyboard.addListener(
      'keyboardWillHide', 
      this.keyboardDidHide.bind(this)
    )
}

componentWillUnmount() {
    this.keyboardDidHideListener.remove()
}

keyboardDidHide(e) {
    this.refs.scrollView.scrollTo({y: 0});
}

Here we reset our scroll view back to the top,

enter image description here

@anivaler 2017-05-19 14:57:32

@could you please provide your render() method?

@Manoj Bhardwaj 2018-06-26 08:42:46

Not work with android

@Sherlock 2015-09-15 19:11:51

2017 Answer

The KeyboardAvoidingView is probably the best way to go now. Check out the docs here. It is really simple compared to Keyboard module which gives Developer more control to perform animations. Spencer Carli demonstrated all the possible ways on his medium blog.

2015 Answer

The correct way to do this in react-native does not require external libraries, takes advantage of native code, and includes animations.

First define a function that will handle the onFocus event for each TextInput (or any other component you would like to scroll to):

// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
  setTimeout(() => {
    let scrollResponder = this.refs.scrollView.getScrollResponder();
    scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
      React.findNodeHandle(this.refs[refName]),
      110, //additionalOffset
      true
    );
  }, 50);
}

Then, in your render function:

render () {
  return (
    <ScrollView ref='scrollView'>
        <TextInput ref='username' 
                   onFocus={this.inputFocused.bind(this, 'username')}
    </ScrollView>
  )
}

This uses the RCTDeviceEventEmitter for keyboard events and sizing, measures the position of the component using RCTUIManager.measureLayout, and calculates the exact scroll movement required in scrollResponderInputMeasureAndScrollToKeyboard.

You may want to play around with the additionalOffset parameter, to fit the needs of your specific UI design.

@miracle2k 2015-09-16 21:44:33

This is a nice find, but for me it wasn't enough, because while the ScrollView will make sure the TextInput is on screen, the ScrollView was still showing content below the keyboard that the user couldn't scroll to. Setting the ScrollView property "keyboardDismissMode=on-drag" allows the user to dismiss the keyboard, but if there is not enough scroll content below the keyboard, the experience is a bit jarring. If the ScrollView only has to scroll because of the keyboard in the first place, and you disable bouncing then there seems to be no way to dismiss the keyboard & show the content below

@Sherlock 2015-09-30 19:58:49

@miracle2k - I have a function that resets the scroll view position when an input is blurred, i.e. when the keyboard closes. Maybe this might help in your case?

@Ryan McDermott 2015-10-09 15:40:29

@Sherlock What does that blur scroll view reset function look like? Awesome solution by the way :)

@Hussian Shaik 2015-11-12 10:48:10

@sherlock i write the code as per you mentioned above,But i got an error : Argument 0 (NSNumber) of RCTUIManager.measureLayout must not be null

@Sherlock 2015-11-12 14:29:00

@HussainShaik - I think you're hitting this issue github.com/facebook/react-native/issues/355 but i'm not too sure, maybe step into the scrollResponderScrollNativeHandleToKeyboard call to double check

@AbM 2015-11-26 19:14:29

this blog has been really helpful

@Rocklan 2015-12-17 05:11:54

Works great in portrait mode, in landscape it scrolls off the screen.

@Dev01 2016-03-20 01:30:23

When I click the "done" button on the keyboard the scrollview doesn't scroll back down even though the keyboard is no longer visible. Has anyone gotten around that?

@amirfl 2016-05-21 15:06:37

In newer React Native versions you'll need to call: * import ReactNative from 'react-native'; * before calling * ReactNative.findNodeHandle() * Otherwise the app will crash

@Varun Gupta 2016-05-25 11:47:42

@Sherlock I used your solution above and it works on the iPhone simulator but when I ported the app to an actual iPhone, the screen doesn't scroll up when I focus on a TextInput and it gets hidden behind the keyboard. I am using iPhone 5s running iOS 9.3.2. I am not sure how to debug the problem. Any hints?

@paulshen 2016-06-01 22:06:43

@VarunGupta i ran into the same thing. Increasing the timeout (50 to 100) worked for me. Maybe that'll work for you too.

@Varun Gupta 2016-06-02 17:23:32

@paulshen thanks for the comment. It has been pointed to me already in a separate question that I asked in stack ooverflow for the same thing but you are right nevertheless.

@Chiranjhivi Ghimire 2016-06-14 06:04:50

@Sherlock i got error as undefined is not a function(evaluating '_react2.default.findNodeHandle(_this2.refs[refName])') . i had applied it in android. Whats is my error is?

@Deepika Patel 2016-07-13 07:31:24

Really help full and short code. Thanks

@antoine129 2016-09-29 10:03:54

Now import {findNodeHandle} from 'react-native' stackoverflow.com/questions/37626851/…

@ODelibalta 2017-10-22 19:55:16

Is there an updated version of this solution? It does not work for me. Keep getting red screen. This is a bit frustrating. I'd image a mobile library would handle the freaking keyboard in their native components but I was wrong.

@Noitidart 2018-05-29 02:20:52

Using 2017 solutions seems worst than 2015. In 2017, it seems when you move focus to the next field though, it doesnt scroll to adjust for that next field. So does this mean 2015 solution is the better one?

@Adrian Zghibarta 2017-01-29 20:40:00

Maybe is to late, but the best solution is to use a native library, IQKeyboardManager

Just drag and drop IQKeyboardManager directory from demo project to your iOS project. That's it. Also you can setup some valus, as isToolbar enabled, or the space between text input and keyboard in the AppDelegate.m file. More details about customisation are in the GitHub page link that I've added.

@loevborg 2017-08-18 18:05:41

This is an excellent option. Also see github.com/douglasjunior/react-native-keyboard-manager for a version wrapped for ReactNative - it's easy to install.

@farwayer 2016-07-09 22:17:20

Facebook open sourced KeyboardAvoidingView in react native 0.29 to solve this problem. Documentation and usage example can be found here.

@Renato Back 2016-07-25 04:04:39

Beware of KeyboardAvoidingView, it just ain't easy to use. It doesn't always behave as you expect it should. Documentation is practically nonexistent.

@antoine129 2016-09-29 08:47:38

doc and behavior are getting better now

@Marc 2016-10-05 22:01:24

The problem I have, is that the KeyboardAvoidingView measures the keyboard height as 65 on my iPhone 6 simulator and so my view is still hidden behind the keyboard.

@Marc 2016-10-05 22:31:49

Only way I could manager it was through a bottompadding approach triggered by DeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));

@Aakash Sigdel 2016-07-25 06:21:11

Just wanted to mention, now there is a KeyboardAvoidingView in RN. Just import it and use it as any other module in RN.

Here is the link to the commit on RN:

https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e

It is available from 0.29.0

They have also included an example on UIExplorer.

@amirfl 2016-05-21 15:25:51

react-native-keyboard-aware-scroll-view solved the problem for me. react-native-keyboard-aware-scroll-view on GitHub

@Renguang Dong 2016-04-26 07:48:25

I also meet this question. Finally, I resolve it by defining the height of each scene, such as:

<Navigator ... sceneStyle={{height: **}} />

And, I also use a third-party module https://github.com/jaysoo/react-native-extra-dimensions-android to get the real height.

@pomo 2016-03-05 09:22:47

Try this:

import React, {
  DeviceEventEmitter,
  Dimensions
} from 'react-native';

...

getInitialState: function() {
  return {
    visibleHeight: Dimensions.get('window').height
  }
},

...

componentDidMount: function() {
  let self = this;

  DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
    self.keyboardWillShow(e);
  });

  DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
      self.keyboardWillHide(e);
  });
}

...

keyboardWillShow (e) {
  let newSize = Dimensions.get('window').height - e.endCoordinates.height;
  this.setState({visibleHeight: newSize});
},

keyboardWillHide (e) {
  this.setState({visibleHeight: Dimensions.get('window').height});
},

...

render: function() {
  return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}

...

It worked for me. The view basically shrinks when the keyboard is displayed, and grows back again when its hidden.

@pomo 2016-03-11 08:22:47

Also, this solution works well (RN 0.21.0) stackoverflow.com/a/35874233/3346628

@animekun 2016-05-16 14:21:06

use this.keyboardWillHide.bind(this) instead of self

@Madura Pradeep 2018-08-27 11:43:31

Use Keyboard instead DeviceEventEmitter

@John kendall 2015-11-07 17:17:30

We combined some of the code form react-native-keyboard-spacer and the code from @Sherlock to create a KeyboardHandler component that can be wrapped around any View with TextInput elements. Works like a charm! :-)

/**
 * Handle resizing enclosed View and scrolling to input
 * Usage:
 *    <KeyboardHandler ref='kh' offset={50}>
 *      <View>
 *        ...
 *        <TextInput ref='username'
 *          onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
 *        ...
 *      </View>
 *    </KeyboardHandler>
 * 
 *  offset is optional and defaults to 34
 *  Any other specified props will be passed on to ScrollView
 */
'use strict';

var React=require('react-native');
var {
  ScrollView,
  View,
  DeviceEventEmitter,
}=React;


var myprops={ 
  offset:34,
}
var KeyboardHandler=React.createClass({
  propTypes:{
    offset: React.PropTypes.number,
  },
  getDefaultProps(){
    return myprops;
  },
  getInitialState(){
    DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
      if (!frames.endCoordinates) return;
      this.setState({keyboardSpace: frames.endCoordinates.height});
    });
    DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
      this.setState({keyboardSpace:0});
    });

    this.scrollviewProps={
      automaticallyAdjustContentInsets:true,
      scrollEventThrottle:200,
    };
    // pass on any props we don't own to ScrollView
    Object.keys(this.props).filter((n)=>{return n!='children'})
    .forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});

    return {
      keyboardSpace:0,
    };
  },
  render(){
    return (
      <ScrollView ref='scrollView' {...this.scrollviewProps}>
        {this.props.children}
        <View style={{height:this.state.keyboardSpace}}></View>
      </ScrollView>
    );
  },
  inputFocused(_this,refName){
    setTimeout(()=>{
      let scrollResponder=this.refs.scrollView.getScrollResponder();
      scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
        React.findNodeHandle(_this.refs[refName]),
        this.props.offset, //additionalOffset
        true
      );
    }, 50);
  }
}) // KeyboardHandler

module.exports=KeyboardHandler;

@Kamil Sarna 2015-11-20 16:36:47

Thanks. Works well, indeed!

@seigel 2015-11-24 14:40:53

Anything easy/obvious that would keep the keyboard from showing in an iOS simulator?

@John kendall 2015-11-25 15:42:01

Did you try Command+K (Hardware->Keyboard->Toggle Software Keboard) ?

@Sam 2016-03-09 08:35:24

@Johnkendall Thanks for the code. Works very well.

@CoderDave 2016-06-03 18:34:55

Try the modified version of this here: gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3 I believe this is better because it doesn't rely on any timeouts which I think is fragile imo.

@deanmcpherson 2015-06-11 06:35:51

@Stephen

If you don't mind not having the height animate at exactly the same rate that the keyboard appears, you can just use LayoutAnimation, so that at least the height doesn't jump into place. e.g.

import LayoutAnimation from react-native and add the following methods to your component.

getInitialState: function() {
    return {keyboardSpace: 0};
  },
   updateKeyboardSpace: function(frames) {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: frames.end.height});
  },

  resetKeyboardSpace: function() {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: 0});
  },

  componentDidMount: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

  componentWillUnmount: function() {
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

Some example animations are (I'm using the spring one above):

var animations = {
  layout: {
    spring: {
      duration: 400,
      create: {
        duration: 300,
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.opacity,
      },
      update: {
        type: LayoutAnimation.Types.spring,
        springDamping: 400,
      },
    },
    easeInEaseOut: {
      duration: 400,
      create: {
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.scaleXY,
      },
      update: {
        type: LayoutAnimation.Types.easeInEaseOut,
      },
    },
  },
};

UPDATE:

See @sherlock's answer below, as of react-native 0.11 the keyboard resizing can be solved using built in functionality.

@aintnorest 2015-07-29 06:08:14

I use brysgo answer to raise the bottom of my scrollview. Then I use the onScroll to update the current position of the scrollview. I then found this React Native: Getting the position of an element to get the position of the textinput. I then do some simple math to figure out if the input is in the current view. Then I use scrollTo to move the minimum amount plus a margin. It's pretty smooth. Heres the code for the scrolling portion:

            focusOn: function(target) {
                return () => {
                    var handle = React.findNodeHandle(this.refs[target]);
                    UIManager.measureLayoutRelativeToParent( handle, 
                        (e) => {console.error(e)}, 
                        (x,y,w,h) => {
                            var offs = this.scrollPosition + 250;
                            var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
                            var headerHeight = Sizes.height / 9;
                            var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
                            var shortSpace = largeSpace - this.keyboardOffset;
                            if(y+h >= this.scrollPosition + shortSpace) {
                                this.refs.sv.scrollTo(y+h - shortSpace + 20);
                            }
                            if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
                        }
                     );
                };
            },

@Stirman 2015-06-22 21:48:43

I'm using a simpler method, but it's not animated yet. I have a component state called "bumpedUp" which I default to 0, but set to 1 when the textInput gets focus, like this:

On my textInput:

onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}

I also have style that gives the wrapping container of everything on that screen a bottom margin and negative top margin, like this:

mythingscontainer: {
  flex: 1,
  justifyContent: "center",
  alignItems: "center",
  flexDirection: "column",
},
bumpedcontainer: {
  marginBottom: 210,
  marginTop: -210,
},

And then on the wrapping container, I set the styles like this:

<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>

So, when the "bumpedUp" state gets set to 1, the bumpedcontainer style kicks in and moves the content up.

Kinda hacky and the margins are hardcoded, but it works :)

@shohey1226 2015-06-13 01:29:09

I used TextInput.onFocus and ScrollView.scrollTo.

...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
  this.refs.scrollView.scrollTo(width*2/3);
},

@mutp 2015-12-03 17:03:45

Seems to work just fine!

@brysgo 2015-04-16 15:17:05

First you need to install react-native-keyboardevents.

  1. In XCode, in the project navigator, right click Libraries ➜ Add Files to [your project's name] Go to node_modules ➜ react-native-keyboardevents and add the .xcodeproj file
  2. In XCode, in the project navigator, select your project. Add the lib*.a from the keyboardevents project to your project's Build Phases ➜ Link Binary With Libraries Click .xcodeproj file you added before in the project navigator and go the Build Settings tab. Make sure 'All' is toggled on (instead of 'Basic'). Look for Header Search Paths and make sure it contains both $(SRCROOT)/../react-native/React and $(SRCROOT)/../../React - mark both as recursive.
  3. Run your project (Cmd+R)

Then back in javascript land:

You need to import the react-native-keyboardevents.

var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;

Then in your view, add some state for the keyboard space and update from listening to the keyboard events.

  getInitialState: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
      this.setState({keyboardSpace: frames.end.height});
    });
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
      this.setState({keyboardSpace: 0});
    });

    return {
      keyboardSpace: 0,
    };
  },

Finally, add a spacer to your render function beneath everything so when it increases size it bumps your stuff up.

<View style={{height: this.state.keyboardSpace}}></View>

It is also possible to use the animation api, but for simplicity's sake we just adjust after the animation.

@Stephen 2015-05-16 16:13:41

It would be awesome to see a code example/some more info on how to do the animation. The jump is pretty janky, and working with only the "will show" and "did show" methods, I can't quite figure out how to guess how long the keyboard animation will be or how tall it is from "will show".

@Jed Lau 2015-09-01 17:54:53

[email protected] now sends keyboard events (e.g., "keyboardWillShow") through the DeviceEventEmitter, so you can register listeners for these events. When dealing with a ListView, however, I found that calling scrollTo() on the ListView's scrollview worked better: this.listView.getScrollResponder().scrollTo(rowID * rowHeight); This gets called on a row's TextInput when it receives an onFocus event.

@Sherlock 2015-09-15 19:14:43

This answer is no longer valid, as the RCTDeviceEventEmitter does the job.

Related Questions

Sponsored Content

4 Answered Questions

0 Answered Questions

How to constant modal position when keyboard is open in React-Native

0 Answered Questions

Auto scrolling when focus on TextInput in scrollview in react native

  • 2018-12-08 01:56:32
  • Water Flower
  • 320 View
  • 1 Score
  • 0 Answer
  • Tags:   react-native

3 Answered Questions

[SOLVED] React Native Card Carousel view?

10 Answered Questions

[SOLVED] React-Native, Android, Genymotion: ADB server didn't ACK

2 Answered Questions

[SOLVED] How to focus a <WebView> in react native?

  • 2018-04-25 14:19:04
  • vladikoff
  • 1472 View
  • 10 Score
  • 2 Answer
  • Tags:   react-native

1 Answered Questions

[SOLVED] Android - trigger adjustResize

0 Answered Questions

TextInput focuses on scroll in React Native

  • 2017-09-20 13:29:59
  • Snowman
  • 225 View
  • 3 Score
  • 0 Answer
  • Tags:   react-native

Sponsored Content