11

I currently have a KeyboardAvoidingView with a hard-coded keyboardVerticalOffset of 64. This works fine on the iPhone but is about 20px short on the iPhone X.

The component looks like this:

<KeyboardAvoidingView behavior='padding' keyboardVerticalOffset={ 64 }>
  <View style={ styles.messageList }>
    ...
  </View>
  <View style={ styles.messageInput }>
    ...
  </View>
</KeyboardAvoidingView>

Is there a better way to determine what keyboardVerticalOffset should be than hard coding a value? Is there something else I could be doing differently with component placement? I'm open to any suggestions.

iPhone 8

enter image description here

iPhone X

enter image description here

anthonator
  • 4,915
  • 7
  • 36
  • 50
  • Another thing to consider is that the keyboard can change size as you change the keyboard type. I'm not familiar with how to handle that in reactNative, but in native iOS code, you would register as an observer for the `UIKeyboardWillChangeFrameNotification` notification. The userInfo you would get with the notification would give you details of the keyboard frame. https://stackoverflow.com/a/35427662/3708242. But I'm not sure if there is a way to capture notifications in react native. – wottle Dec 13 '17 at 20:37
  • Did you ever figure out a solution to this? – Lorenz Jan 26 '18 at 01:13

4 Answers4

22

This is caused by the status bar height being different for iphoneX. (you also get the same issue with other iphones if you toggle the 'in-call' status bar using ⌘Y in the simulator).

You can get the status bar height and use this to set the keyboardVerticalOffset value of the KeyboardAvoidingView. (in our case this was 44 + statusBarHeight)

import React, {Component} from 'react';
import {KeyboardAvoidingView, NativeModules, StatusBarIOS} from 'react-native';

const {StatusBarManager} = NativeModules;

export class IOSKeyboardAvoidingView extends Component {

  state = {statusBarHeight: 0};

  componentDidMount() {
    StatusBarManager.getHeight((statusBarFrameData) => {
      this.setState({statusBarHeight: statusBarFrameData.height});
    });
    this.statusBarListener = StatusBarIOS.addListener('statusBarFrameWillChange', (statusBarData) => {
      this.setState({statusBarHeight: statusBarData.frame.height});
    });
  }

  componentWillUnmount() {
    this.statusBarListener.remove();
  }

  render() {
    const {style, children} = this.props;
    return (
      <KeyboardAvoidingView
        behavior="padding"
        keyboardVerticalOffset={44 + this.state.statusBarHeight}
        style={style}
      >{children}
      </KeyboardAvoidingView>
    );
  }
}
TimT
  • 1,576
  • 16
  • 14
6

Please refer to : https://stackoverflow.com/a/51169574/10031014 for similar issues

Benson Toh
  • 1,970
  • 1
  • 10
  • 13
3

I have used a custom component to overcome this situation.

import React from "react";
import {Animated, Keyboard} from "react-native";

export default class KeyboardAwareComponent extends React.Component {

    constructor(props) {
        super(props)
        this.keyboardHeight = new Animated.Value(0);
    }

    componentWillMount () {
        this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
        this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
    }

    componentWillUnmount() {
        this.keyboardWillShowSub.remove();
        this.keyboardWillHideSub.remove();
    }

    keyboardWillShow = (event) => {
        Animated.parallel([
            Animated.timing(this.keyboardHeight, {
                duration: event.duration,
                toValue: event.endCoordinates.height,
            })
        ]).start();
    };

    keyboardWillHide = (event) => {
        Animated.parallel([
            Animated.timing(this.keyboardHeight, {
                duration: event.duration,
                toValue: 0,
            })
        ]).start();
    };

    render(){
        const {children, style, ...props} = this.props
        return(
            <Animated.View style={[{flex:1,alignItems:'center',paddingBottom: this.keyboardHeight},style]} {...props}>
                {children}
            </Animated.View>
        );
    }

}

Just use the component "KeyboardAwareComponent" as a root component of any page. It will automatically adjust the view when keyboard will show or hide.

Example:---

YourComponent extends React.Component{
    render(){
        <KeyboardAwareComponent>
            {Your child views}
        </KeyboardAwareComponent>
    }
}
srs
  • 2,521
  • 16
  • 16
0

So I did a quick check, given my understanding of how to do this in native iOS, and it seems like in newer versions of react native, you can do this relatively easily.

There do seem to be a couple of options, depending on your flexibility needs.

First, have you tried using KeyboardAvoidView instead of a standard container View without specifying keyboardVerticalOffset?

Another option that gives you much more control (similar to what I would do in a native iOS app) is to use the Keyboard module to create listeners on the keyboard events.

  componentWillMount () {
    this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
    this.keyboardWillChangeSub = Keyboard.addListener('keyboardWillChangeFrame', this.keyboardWillChange);
    this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
  }

  componentWillUnmount() {
    this.keyboardWillShowSub.remove();
    this.keyboardWillChangeSub.remove();
    this.keyboardWillHideSub.remove();
  }

This would allow you to get the keyboard height from the event parameter:

keyboardWillShow = (event) => {
    Animated.parallel([
      Animated.timing(this.keyboardHeight, {
        duration: event.duration,
        toValue: event.endCoordinates.height,
      }),
      Animated.timing(this.imageHeight, {
        duration: event.duration,
        toValue: IMAGE_HEIGHT_SMALL,
      }),
    ]).start();
  };

Repeat something similar for keyboardWillChange and keyboardWillHide.

For a better, more detailed explanation of your options, see this page: https://medium.freecodecamp.org/how-to-make-your-react-native-app-respond-gracefully-when-the-keyboard-pops-up-7442c1535580

I think the best first test would be to try to remove the keyboardVerticalOffset before trying to add code to handle the keboard events.

wottle
  • 13,095
  • 4
  • 27
  • 68
  • Yes, I have tried using KeyboardAvoidingView without the keyboardVerticalOffset. Without keyboardVerticalOffset the keyboard covers the "Message" portion entirely. – anthonator Dec 14 '17 at 14:46
  • Are you running the latest version of react native? If not, I would try updating to see if this has been addressed. Then, I would try the approach with the Keyboard events where you can see exactly what is going on with the keyboard frame and adjust your UI components appropriately. – wottle Dec 14 '17 at 15:09