◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-04-15中級

Rorkアプリでキーボードが入力フィールドを隠す問題の完全対処ガイド

Rorkアプリでキーボードが表示されるとテキスト入力フィールドが隠れる問題の原因と解決策を解説します。KeyboardAvoidingView・ScrollView・useKeyboard フックを使った実践的な対処法を紹介します。

Rork584キーボードKeyboardAvoidingViewReact Native239Expo219トラブルシューティング85フォーム3

フォーム画面を作ったとき、メールアドレスや本文を入力しようとするとキーボードが開いて下のフィールドが完全に見えなくなる——Rork でアプリを開発していると必ずといっていいほど直面する問題です。しかもこの現象、iOS と Android で挙動が微妙に違うので、片方で直したつもりがもう片方で再発する、という厄介な性格があります。

発生パターン別に原因を整理し、実際に動くコードとともに解決策を順番にご紹介します。

なぜキーボードが入力フィールドを隠すのか

React Native(Rork が内部で使うフレームワーク)では、ソフトウェアキーボードが展開されてもレイアウトは自動的に押し上げられません。Webブラウザとは異なり、キーボードの領域分だけ表示エリアが縮む仕組みがデフォルトで有効化されているわけではないのです。

iOS は behavior="padding" で比較的素直に対応できますが、Android は windowSoftInputMode の設定によって挙動が変わります。Expo managed workflow では app.json の設定でコントロールできます。

まず確認すること:app.json の softwareKeyboardLayoutMode

{
  "expo": {
    "android": {
      "softwareKeyboardLayoutMode": "pan"
    }
  }
}

Expo では softwareKeyboardLayoutMode を "pan" にするとキーボード表示時に画面全体が上にパンします。"resize" にするとコンテンツ領域が縮小されます。どちらが適しているかはアプリの構造によって変わりますが、ほとんどのフォーム画面では "pan" のほうが自然に見えます。

ただし "pan" にしても iOS では効果がなく、Android 専用の設定です。iOS には別のアプローチが必要です。

基本パターン:KeyboardAvoidingView を使う

import {
  KeyboardAvoidingView,
  Platform,
  ScrollView,
  TextInput,
  StyleSheet,
  View,
} from 'react-native';
 
export default function ContactForm() {
  return (
    // Platform.OS で iOS / Android を分岐するのがポイント
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}
    >
      <ScrollView
        contentContainerStyle={styles.scrollContent}
        keyboardShouldPersistTaps="handled"
      >
        <TextInput
          style={styles.input}
          placeholder="名前"
          returnKeyType="next"
        />
        <TextInput
          style={styles.input}
          placeholder="メールアドレス"
          keyboardType="email-address"
          returnKeyType="next"
        />
        <TextInput
          style={styles.input}
          placeholder="お問い合わせ内容"
          multiline
          numberOfLines={6}
          textAlignVertical="top"
        />
      </ScrollView>
    </KeyboardAvoidingView>
  );
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  scrollContent: {
    padding: 16,
    paddingBottom: 40, // キーボードが閉じたあとの余白
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 12,
    marginBottom: 16,
    fontSize: 16,
  },
});

重要なポイントが2つあります。

  1. behavior プロパティを必ず Platform.OS で分岐すること。iOS は 'padding'、Android は 'height' が基本です。undefined のままにすると iOS でまったく機能しません。

  2. keyboardVerticalOffset でナビゲーションバーの高さを調整すること。Expo Router のスタック画面では通常 90 前後が適切ですが、カスタムヘッダーを使っている場合は実測して合わせてください。

よくある間違い:ScrollView を忘れる

KeyboardAvoidingView だけ追加しても、中に ScrollView がなければ画面の内容が画面外に飛び出したとき追いかけられません。フォームの項目が画面に収まりきらない場合は必ず ScrollView を組み合わせてください。

また keyboardShouldPersistTaps="handled" を ScrollView に設定しないと、フィールド外をタップしてもキーボードが閉じなかったり、タップがボタンに届かないトラブルが起きます。これは忘れがちな設定です。

パターン2:useKeyboard フックで柔軟に制御する

KeyboardAvoidingView の動作が思い通りにならないケース(ボトムシートと組み合わせた時、アニメーション中にズレるなど)では、キーボードの高さを直接取得して自分でレイアウトを調整する方法が確実です。

import { useEffect, useState } from 'react';
import { Keyboard, KeyboardEvent, Platform } from 'react-native';
 
// キーボードの高さをリアルタイムに取得するカスタムフック
export function useKeyboardHeight() {
  const [keyboardHeight, setKeyboardHeight] = useState(0);
 
  useEffect(() => {
    const showEvent =
      Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
    const hideEvent =
      Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
 
    const showSub = Keyboard.addListener(
      showEvent,
      (e: KeyboardEvent) => {
        setKeyboardHeight(e.endCoordinates.height);
      }
    );
    const hideSub = Keyboard.addListener(hideEvent, () => {
      setKeyboardHeight(0);
    });
 
    return () => {
      showSub.remove();
      hideSub.remove();
    };
  }, []);
 
  return keyboardHeight;
}
// 使い方
export default function ChatScreen() {
  const keyboardHeight = useKeyboardHeight();
 
  return (
    <View style={{ flex: 1 }}>
      <FlatList
        data={messages}
        // ...
      />
      <View
        style={{
          paddingBottom: keyboardHeight > 0 ? keyboardHeight : 16,
          borderTopWidth: 1,
          borderTopColor: '#eee',
          padding: 8,
        }}
      >
        <TextInput placeholder="メッセージを入力..." />
      </View>
    </View>
  );
}

チャット画面のような「入力欄が画面下部に固定されている」UIではこの方法が最も安定します。keyboardHeight が変化するたびに再レンダリングが走りますが、チャット画面程度ではパフォーマンスへの影響は無視できます。

Rork Max(SwiftUI)の場合

Rork Max で SwiftUI ネイティブアプリを生成している場合は、React Native のアプローチは関係ありません。SwiftUI では .ignoresSafeArea(.keyboard) と ScrollView の組み合わせが基本です。

// SwiftUI でキーボード対応フォームを作る例
struct ContactFormView: View {
    @State private var name = ""
    @State private var email = ""
    @State private var message = ""
 
    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                TextField("名前", text: $name)
                    .textFieldStyle(.roundedBorder)
                TextField("メールアドレス", text: $email)
                    .textFieldStyle(.roundedBorder)
                    .keyboardType(.emailAddress)
                TextEditor(text: $message)
                    .frame(height: 120)
                    .border(Color.gray.opacity(0.3))
            }
            .padding()
        }
        // これがないと TextEditor が画面下に来たとき隠れる
        .ignoresSafeArea(.keyboard, edges: .bottom)
    }
}

SwiftUI は iOS 15 以降、デフォルトでキーボード回避機能が内蔵されていますが、TextEditor(複数行テキスト入力)では明示的に .ignoresSafeArea(.keyboard) を指定しないと下部が隠れることがあります。

タブバーがあるとさらに複雑になる

タブベースのナビゲーション(expo-router の Tabs など)では、タブバーの高さ分だけ追加のオフセットが必要です。

import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
function FormWithTabs() {
  const insets = useSafeAreaInsets();
  const TAB_BAR_HEIGHT = 49; // Expo Router のデフォルト
 
  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      // タブバー + セーフエリアの合計を引く
      keyboardVerticalOffset={TAB_BAR_HEIGHT + insets.bottom}
    >
      {/* フォームの内容 */}
    </KeyboardAvoidingView>
  );
}

keyboardVerticalOffset は「キーボードの基準位置をどこにするか」のオフセットです。タブバーがある画面では、タブバーの高さを正確に引いてあげないと計算がズレます。

全体を振り返って:選択肢を状況で使い分ける

最後に、どのアプローチを選ぶべきかの判断基準をまとめます。

  • シンプルなフォーム(ログイン・問い合わせ): KeyboardAvoidingView + ScrollView で十分対応できます。
  • チャット画面・下部固定の入力欄: useKeyboardHeight フックで直接高さを管理するほうが安定します。
  • Rork Max(SwiftUI): .ignoresSafeArea(.keyboard) を使い、SwiftUIの仕組みに乗りましょう。
  • タブバーありの画面: keyboardVerticalOffset にタブバーの高さを加算することを忘れないでください。

フォームのUI/UXをさらに磨きたい場合は、Rorkアプリのレイアウト・レスポンシブデザインのトラブルシューティングガイドも参考にしてください。入力バリデーションの実装についてはreact-hook-form と Zod を使ったフォームバリデーションで詳しく解説しています。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-07
日本語の見出しだけ太字にならない — Android で fontWeight が効かないときの切り分けと直し方
Rork で作ったアプリで、iPhone では太字なのに Android では日本語の見出しが細いままになる症状の切り分け記事です。原因の見分け方、fontFamily を重みごとに分ける直し方、確認用コードを載せています。
⬡ 開発ツール2026-09-21
exit code 0 で止まるビルド — ログを黙らせていた設定と、空ファイルを通した存在チェック
eas build --local が exit code 0 but produced no further output で止まる件と、0 バイトのスタブが埋め込まれず dyld で落ちる件。どちらも原因は出力を減らす設定でした。自分のスクリプトを棚卸しする手順まで書き残します。