hackio hackio - 3 months ago 58
React JSX Question

How to position input onFocus so that it won't stay behind the keyboard [React-Native]

I am having difficulties with forms in my react-native ios app. I want my inputs to position themselves onFocus so that they will be visible when keyboard opens. I used scrollResponderScrollNativeHandleToKeyboard which was recommended in other issues, but it is really buggy. Sometimes it works sometimes it doesn't.

inputFocused(refName) {
let scrollResponder = this.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
ReactNative.findNodeHandle(this[refName]),
270,
true
);
}

Answer

I had the same problem. I started out with this solution, but sometimes it didn't work. I made some slight modifications, mainly removing the 50ms timeout. And now I haven't had a problem.

/**
 * Based on http://stackoverflow.com/a/33585501/1783214
 *
 * 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';
import React, {Component} from 'react';

import ReactNative, {
    ScrollView,
    View,
    DeviceEventEmitter,
} from 'react-native';

class KeyboardHandler extends Component {
    constructor(props) {
        super(props);
        this.state = {keyboardSpace: 0};
        this.focused = null;
        this._didShowListener = null;
        this._willHideListener = null;
    }

    onKeyboarDidShow(frames) {
        if (!frames.endCoordinates || !this.focused) {
            return;
        }
        this.setState({keyboardSpace: frames.endCoordinates.height});
        let scrollResponder = this.refs.scrollView.getScrollResponder();
        scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
            this.focused,
            this.props.offset, //additionalOffset
            true
        );
    }

    onKeyboardWillHide() {
        this.setState({keyboardSpace: 0});
    }

    componentWillMount() {
        this._didShowListener = DeviceEventEmitter.addListener('keyboardDidShow', this.onKeyboarDidShow.bind(this));
        this._willHideListener = DeviceEventEmitter.addListener('keyboardWillHide', this.onKeyboardWillHide.bind(this));

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

    componentWillUnmount() {
        this._didShowListener.remove();
        this._willHideListener.remove();
    }

    render() {
        return (
            <ScrollView ref='scrollView' {...this.scrollviewProps}>
                {this.props.children}
                <View style={{ height: this.state.keyboardSpace }}/>
            </ScrollView>
        );
    }

    inputFocused(_this, refName) {
        this.focused = ReactNative.findNodeHandle(_this.refs[refName]);
    }

    static propTypes = {
        offset: React.PropTypes.number
    };

    static defaultProps = {
        offset: 0
    };
}

export default KeyboardHandler;

We have not upgraded to RN 0.27 yet, but according to the changelog DeviceEventEmitter is deprecated in 0.27 and needs to be replaced with Keyboard:

DeviceEventEmitter.addListener(...) becomes Keyboard.addListener(...) with the same parameters.

Comments