React NativeでKeyboardAvoidingViewが意図通りに動かない場合
React Nativeの「KeyboardAvoidingView」についてです。
スマホではテキストフォームにフォーカスしたときに、画面の下半分くらいはキーボードに隠れてしまいます。 「KeyboardAvoidingView」はキーボードを避けるように上に移動してくれるコンポーネントです。
とても便利で欠かせないものなのですが、実装に苦戦することがあります。実装したことのある人ならわかってもらえると思いますが、意図通りの挙動にならない場合が多いです。なんかクセがあります。
いくつかアプリを作る中で、上手くいくパターンや確認すべきポイントがわかってきたのでまとめておきます。
概要
- ドキュメント
- 「keyboardVerticalOffset」を指定する
- padding
- height
- position
- 動作を指定するパラメータ「behavior」
ドキュメント
https://docs.expo.io/versions/v36.0.0/react-native/keyboardavoidingview/
Expoのドキュメントです。プロパティの定義などが書かれています。情報は少ないです。
https://medium.com/@nickyang0501/keyboardavoidingview-not-working-properly-c413c0a200d4
以前見つけた記事です。正常に動作しない場合の解決すべきパターンが書かれていました。
記事によるとjustifyContentなどの設定も関連しています。
全体を囲うべきなのか必要な部分だけ囲うべきなのかなど、ベストな使い方が知りたい。
動作を指定するパラメータ 「behavior」
ドキュメントにあるように挙動を制御するパラメータです。 とりあえず、iOSの場合はデフォルトだと上手く動かないです。いずれかを指定するようにしましょう。 個人的にはAndroidでも指定したほうが意図通りに動く印象です。
- position
- padding
- height
それぞれの設定の挙動を以下に記載します。
position
要素の中をposition:'abusolute'
の状態にして、中の要素が上に動きます。
移動するときはキーボードの上端とKeyboardAvoidingView
の中の要素の下端が一致するように移動し、中の要素が上にはみ出します。中の要素のみが移動してKeyboardAvoidingView
自体は移動していないようです。
要素からはみ出るので兄弟要素に重なることが可能です。 親要素からはみ出すことはできないようです。たとえば、親要素がぴったり同じ大きさだと動きませんでした。Viewなどで囲むだけで動かなくなりました。
ルートやモーダルなど、大きな領域の直下に設置すれば動作するイメージです。はみ出すことができるのでKeyboardAvoidingView
自体の高さは小さくても大丈夫です。
positionを指定すると、position:'absolute',overflow:'visible',
が指定された状態になります。中の要素の位置が上にそろったり、高さがなくなったりしますので注意しましょう。
縦に大きいコンポーネントにすると、画面上部のフォームにフォーカスした際に画面の上にはみ出す場合があります。KeyboardAvoidingView
の全体がキーボードに隠れないように動くので、上端の方は気をつけましょう。
padding
兄弟要素をキーボードの高さ分小さくします。その分KeyboardAvoidingView
が上に移動します。
親要素の中で動き、Flexによりサイズが決まっている兄弟要素を小さします。 親要素をはみ出すことはできませんし、最大でも兄弟要素のサイズ分しか移動しません。
小さくできる兄弟要素がない、兄弟要素がFlexで定義されていない場合は動かないです。 兄弟要素の子要素の大きさ指定は無視されて小さくなります。
同じ親に含まれない要素には影響しません。「おじ」や「いとこ」にあたるような要素は、Flexにしていても大きさが変わりません。
兄弟要素が持っている領域分しか移動ができないので、上にはみ出すことは無いと思います。 縦に大きいコンポーネントにすると、画面下部のフォームではキーボードを避けきれず隠れてしまう場合があります。
height
実際のアプリでは使ったことがありません。記事を書くために使ってみた所、以下の挙動をしました。
KeyboardAvoidingView
の自体の高さが小さくなる挙動でした。キーボードの上端と要素の下端が一致するようにheightが小さくなるようです。
しかし、KeyboardAvoidingView
の高さが小さくなっても下の要素が上に詰めるだけです。KeyboardAvoidingViewの子要素のフォームが上に動くわけではなかったので、結局キーボードに隠れてしまいました。
「keyboardVerticalOffset」を指定する
キーボードを避けるときの移動距離のオフセット値を指定できるパラメータです。 ヘッダーの実装方法やOSにより必要になる場合・不要な場合があります。
基本的に「ちょっと要素が動くんだけど移動が足りなくて隠れたまま」というときは、ここを見直すといいです。キーボードが出てくる一瞬の間に少し動いていることが確認できたら調整してみましょう。
keyboardVerticalOffset={Platform.select({
ios: Header.HEIGHT, // iOS
android:Header.HEIGHT + StatusBar.currentHeight, // android
})}
例えば、react-navigationでヘッダーを配置している場合は上記の様に指定することで正常に動作しました。
画面内に2つ設置した場合
両方が独立して動くため基本的に上手くいきません。
キーボードの上端とそれぞれのKeyboardAvoidingView
の下端が一致するように移動するため、2つのコンポーネントがキーボードの真上で重なってしまう場合があります。
おわりに
スマホアプリを作る上で、入力フォームを実装することは多いです。 入力している内容がキーボードに隠れて見えなくなるのはUI的にイマイチですのでしっかり対応したいです。
そのためにもKeyboardAvoidingViewのコンポーネントは必須です。動作にクセがあるなあと個人的に思っているので、使いこなせるようになっておきたいです。
またはこれに変わるモジュールを探しておくのもいいと思います。オススメあったら教えてほしいです。