【React Native】TextInputがキーボードに隠れてしまう問題の解決法について

# はじめに

みなさま、React Nativeをお使いでしょうか！！？
今回は、React Nativeを使っていて、TextInput周りで苦戦したので、それの対策方法を共有いたします。

この内容はReact Native meetup#3にてお話させて頂いた内容と一部被ります。
発表内容はこちらです。「[リアクトネイティブ　鉄壁のキーボード](https://speakerdeck.com/yutamakotaro/riakutoneiteibu-tie-bi-falsekibodo)」

※基本コードは公式から拝借しています。

# 何が起こるか。

iOSでは、TextInputがキーボードにかぶる問題が発生します。入力は出来るのですが、入力途中の文字は見えませんし、「このアプリちゃんとしてねぇから使いたくないなー」などとUXに大きな影響を与えてしまうことが懸念されます。何より、開発者としてちゃんとしたいことろだと思います。


ちょうどこんな感じ！

<img src="https://qiita-image-store.s3.amazonaws.com/0/121851/b8e7893f-a27e-7a1a-6713-6113b4f6260d.gif" width=300 />


# 対策方法

この問題に対抗するために、以下の方法を紹介します。まだまだ未熟者ですので、他にも良い方法があるかもしれません。

どれも得意とする場面、苦手とする場面があります。そのため三つに分けて紹介させていただきます。

- 複数フォーム（単一も可能）＆ScrollView
- 単一フォーム＆Flex　layout
- 複数フォーム（単一も可能）＆Flex layout



## 単一フォームor複数フォーム　＆　ScrollViewを使っている時の対策

[react-native-keyboard-aware-view ](https://www.npmjs.com/package/react-native-keyboard-aware-view)を使う。

```html
<KeyboardAwareScrollView ref='scroll'>
  <View>
    <TextInput style={styles.input}/>
  </View>
</KeyboardAwareScrollView>
```

ScrollViewコンポーネントはでてきませんが、内部的にはScrollViewが展開されます。
設置するだけで、簡単に動作します。

```js
_scrollToInput (reactNode: any) {
  // Add a 'scroll' ref to your ScrollView
  this.refs.scroll.scrollToFocusedInput(reactNode)
}
```

```html
  <TextInput onFocus={(event: Event) => {
        this._scrollToInput(ReactNative.findNodeHandle(event.target))
  }/>
```
このように、細かい制御をすることもできます。
とても素晴らしいです！！。

ただし、flexを扱うことはできず、UI実装にはheight, widthなどを使って調整をして行く必要あります。



## 単一フォーム＆Flex　レイアウトを使っている時の対策

[react-native-keyboard-aware-scroll-view](https://github.com/Andr3wHur5t/react-native-keyboard-spacer)を使う。

こちらはKeyboardSpacerコンポーネントを配置してキーボードが開くと、これの高さが伸びて画面が上がります。

```html
class DemoApp extends Component {
  render() {
    return (
      <View style={[{flex: 1}]}>
        {/* Some random image to show scaling */}
        <Image source={{uri: 'http://img11.deviantart.net/072b/i/2011/206/7/0/the_ocean_cherry_tree_by_tomcadogan-d41nzsz.png', static: true}}
                 style={{flex: 1}}/>

        {/* The text input to put on top of the keyboard */}
        <TextInput style={{left: 0, right: 0, height: 45}}
              placeholder={'Enter your text!'}/>

        {/* The view that will animate to match the keyboards height */}
        <KeyboardSpacer/>
      </View>
    );
  }
}
```

マニュアル的に、ここは画面をあげる、あげない、キボードとかぶるようならあげるということはできません。
ですが、flexであることは維持しつつキーボードをちょうど被らない位置に持ってくることができるので、よくある画面下部に入力フォームが配置されているようなレイアウトでは非常に効力を発揮します。

## 複数フォーム（単一も可能）＆Flex layout

scrollViewでなくflex Viewかつ複数フォームで対応させたくて、こちらのパッケージを作成しました。
[react-native-flex-keyboard-spacer](https://github.com/YutamaKotaro/react-native-flex-keyboard-spacer)
まだ、できたばかりなので多少バグがあるかもしれません。ドキュメントも更新していきます。

```html
class TextField extends Component {    
   render() {
        const { style, label, spacerProps } = this.props;
        return (
            <View style={[styles.container, style]}>
                <Text style={styles.label}>{label}</Text>
                <TextInput {...spacerProps(this)} style={styles.textInput} />
            </View>
        );
    }
}

const App = (props) => {
    const { spacerProps } = props;
    return (
        <View style={styles.container}>
            <TextField spacerProps={spacerProps[0]} label="form1" style={{ flex: 4 }} />
            <TextField spacerProps={spacerProps[1]} label="form2" style={{ flex: 3 }} />
            <TextField spacerProps={spacerProps[2]} label="form3" style={{ flex: 2 }} />
            <TextField spacerProps={spacerProps[3]} label="form4" style={{ flex: 1 }} />
        </View>
    );
};

export default keyBoardSpacer({
    numbers: 4,
})(App);
```

残念なことに、mesureメソッドを使用しているためTextInputを含んているコンポーネントがSFCである場合は動作しません。

```js
<TextInput {...spacerProps(this)} style={styles.textInput} />
```

スプレッド演算子を使って、props.spacerProps[index]を実行することで、要素の位置を特定して、キボードが表示される場合キーボードと被ってしまうのかを計算して、被ってしまうのであれば被らない位置まで画面を移動させてくれます。
こちらはscrollViewをつかっていないため、flexを使ってUIを構築することができます。

バグ、issueなどあればご報告いただけると修正いたします。また、PRやご意見など頂けましたら可能な限り対応させていただきますので、使っていただけると幸いです。
また、他にはこうゆう方法があるといったことがあればお教え頂けますと幸いです・・・・！！


# 最後に


是非とも快適なRNライフをお送り頂けますよう願っております！！