How to automatically shift the window due to the keyboard when TextInput has focus?

I saw this hack for my own applications to automatically scroll the window, but wonders how best to do this for the response. When the field becomes focus and is low in the view, the keyboard will close the text field. You can see this problem in the UIExplorer TextInputExample.js example. Anyone have a good solution?

+88
javascript react-native
Mar 28 '15 at 3:12
source share
14 answers

2017 Answer

KeyboardAvoidingView is probably the best way to go now. Check out the docs here .

2015 Answer

The correct way to do this in react-native does not require external libraries, it uses its own code and includes animations.

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

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

Used by RCTDeviceEventEmitter for events and keyboard sizes, measures the position of a component using RCTUIManager.measureLayout and calculates the exact scroll movement required in scrollResponderInputMeasureAndScrollToKeyboard .

You can play around with the additionalOffset option to fit the needs of your specific user interface design.

+81
Sep 15 '15 at 19:11
source share

Facebook open sourced KeyboardAvoidingView in native 0.29 answer to solve this problem. An example of documentation and usage can be found here .

+26
Jul 09 '16 at 22:17
source share

We combined some of the code in the form response-native-keyboard-spacer and the code from @Sherlock to create the KeyboardHandler component that you can wrap around any View with TextInput element. 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; 
+12
Nov 07 '15 at 17:17
source share

First you need to install react-native-keyboardevents .

  • In Xcode, in the project navigator, right-click Libraries ➜ Add Files to [your project name] Go to node_modules ➜ reaction-native-keyboardevents and add the .xcodeproj file.
  • In Xcode, in the project navigator, select your project. Add lib * .a from the project keyboard to your project Build Phases ➜ Binary Library Link Click the .xcodeproj file you added earlier in the project navigator and go to the build Settings tab. Make sure "All" is turned on (instead of "Basic"). Find the header search paths and make sure that they contain both $ (SRCROOT) /../ reaction-native / React and $ (SRCROOT) /../../ React-mark as recursive.
  • Run the project (Cmd + R)

Then go back to javascript land:

You need to import messages that respond to keyboard reactions.

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

Then, in your opinion, add some state to the keyboard space and update it when listening to 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 underneath everything, so when it grows in size, it will come across your stuff.

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

It is also possible to use api animation, but for simplicity we just tweak the animation.

+10
Apr 16 '15 at 15:17
source share

response-native-keyboard-aware-scroll-view solved the problem for me. recall-native-keyboard-scroll-view on github

+7
May 21 '16 at 15:25
source share

Try the following:

 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 returns again when it is hidden.

+6
Mar 05 '16 at 9:22
source share

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

Here is a link to commit to RN:

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

It is available from 0.29.0

They also included an example in UIExplorer.

+4
Jul 25 '16 at 6:21
source share

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

Just drag and drop the IQKeyboardManager directory from the demo project into the iOS project. It. You can also configure some valus included by isToolbar, or a space between text input and keyboard in the AppDelegate.m file. For more information on customization, see the link to the GitHub page that I added.

+4
Jan 29 2018-01-17
source share

@Stephen

If you don't mind animating the height at exactly the same speed as the keyboard, you can simply use LayoutAnimation so that at least the height does not snap into place. eg.

import LayoutAnimation from response-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 sample animations (I use 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, since taking into account reaction 0.11, resizing the keyboard can be solved using the built-in functions.

+2
Jun 11 '15 at 6:35
source share

I used TextInput.onFocus and ScrollView.scrollTo.

 ... <ScrollView ref="scrollView"> ... <TextInput onFocus={this.scrolldown}> ... scrolldown: function(){ this.refs.scrollView.scrollTo(width*2/3); }, 
+2
Jun 13 '15 at 1:29
source share

You can combine several methods into something simpler.

Attach the onFocus listener to the inputs

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

Our scroll down method looks something like this:

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

This indicates our scroll (do not forget to add a link) to scroll down to the position of our focused input - 200 (this is approximately 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 view our scroll up,

enter image description here

+2
Jun 23 '16 at 19:39
source share

I am using a simpler method, but it is not yet animated. I have a component state called "bumpedUp" that defaults to 0, but is set to 1 when textInput gets focus, for example:

In my text

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

I also have a style that gives the container for packing everything on this screen a bottom edge and a negative top edge, for example:

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

And then in the packaging container I set the following styles:

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

So, when the "bumpedUp" state is set to 1, the bumpedcontainer style starts and moves the content up.

Kinda hacky and fields are hardcoded, but it works :)

0
Jun 22 '15 at 21:48
source share

I am using brysgo's answer to raise the bottom of my scrollview. Then I use onScroll to update the current scrollview position. Then I found this React Native: getting the position of the element to get the position of the textinput. Then I do some simple math if the input is in its current form. Then I use scrollTo to move the minimum amount plus margin. It is pretty smooth. Here is the code to scroll:

  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 ); } ); }; }, 
0
Jul 29 '15 at 6:08
source share

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

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

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

0
Apr 26 '16 at 7:48
source share



All Articles