◉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-21中級

Rork で作ったアプリがノッチや下部インジケータで切れる時の直し方

Rorkで生成したアプリのタイトルがノッチに重なる、タブバーがホームインジケータに食い込む問題の直し方です。プレビューで気付けない理由、SafeAreaViewで囲む最小修正、useSafeAreaInsetsを使う画面別の対応、実機での確認手順まで解説します。

Rork585Safe AreaReact Native239iOS115Android55UI9

Rork で生成されたプロトタイプを iPhone 15 Pro の実機に流してみて、最初に違和感を覚えるのがこの瞬間ではないでしょうか。画面上部にあるはずのタイトルがノッチに重なり、下部のタブバーがホームインジケータに食い込んでいます。プレビュー画面ではきれいに見えていたのに、実機に出した途端にレイアウトが崩れる — 私自身、初めて Rork で作ったアプリを TestFlight に上げた時、まさにこの問題で30分ほど固まった記憶があります。

このレイアウト崩れは、Rork 固有のバグではありません。React Native / Expo で作るアプリ全般に共通する「Safe Area を考慮していない」という問題で、Rork が生成する初期コードに意図的に最小限の Safe Area 対応しか入っていないために顕在化しやすいのです。ここではなぜこの問題が起きるのかを整理したうえで、Rork で書き換える時にそのまま貼り付けて使える修正パターンをお伝えします。

なぜ Rork のプレビューでは気付けないのか

Rork の Web プレビューは、画面を四角い枠の中に表示する仕様になっています。つまり、プレビューには「ノッチ」「ダイナミックアイランド」「ホームインジケータ」「Android の3ボタンナビゲーション」といった実機特有の領域が存在しません。その結果、プレビューできれいに見えるレイアウトでも、実機に出した瞬間にコンテンツが端末のシステム UI と重なってしまいます。

具体的に重なるのは次の領域です。

  • iPhone X 以降のノッチ・ダイナミックアイランド領域(上部約 44〜59pt)
  • iPhone のホームインジケータ領域(下部約 34pt)
  • Android のステータスバー(上部約 24dp)
  • Android のナビゲーションバー(下部約 48dp、ジェスチャーナビゲーションは異なる)

これらの領域を避けてコンテンツを配置するための仕組みが、React Native では SafeAreaView と useSafeAreaInsets の2つです。Rork 生成コードでは片方しか入っていなかったり、ネスト構造の都合で効かなかったりするケースが多く見られます。

最小限で直す: SafeAreaView で囲むだけのパターン

一番シンプルな修正は、画面全体を SafeAreaView で囲むことです。ヘッダーもフッターもない一覧画面なら、これだけで直ります。

import { SafeAreaView } from "react-native-safe-area-context";
import { ScrollView, View, Text, StyleSheet } from "react-native";
 
export default function ArticleList() {
  return (
    <SafeAreaView style={styles.safe} edges={["top", "bottom"]}>
      <ScrollView>
        <View style={styles.header}>
          <Text style={styles.title}>記事一覧</Text>
        </View>
        {/* 記事リスト */}
      </ScrollView>
    </SafeAreaView>
  );
}
 
const styles = StyleSheet.create({
  safe: { flex: 1, backgroundColor: "#fff" },
  header: { padding: 16 },
  title: { fontSize: 22, fontWeight: "700" },
});

ポイントは react-native-safe-area-context の SafeAreaView を使うことです。React Native 標準の SafeAreaView は iOS 専用のため、Android では効きません。Expo プロジェクトではデフォルトで react-native-safe-area-context が入っているので、そのまま import すれば動きます。

edges プロパティで、どの方向の safe area を適用するかを選べます。ボトムタブバーがある画面では edges={["top"]} を指定し、下部は自分で処理すると後述のタブバー対応がスムーズになります。

タブバー・ヘッダーがある画面: useSafeAreaInsets を使う

タブバーやカスタムヘッダーがある画面は、SafeAreaView だけでは不十分です。コンテンツ領域とバー領域で別々の余白計算が必要になるため、useSafeAreaInsets で数値を取得して手動で当てます。

import { useSafeAreaInsets } from "react-native-safe-area-context";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
 
export default function TabBarLayout({ children }: { children: React.ReactNode }) {
  const insets = useSafeAreaInsets();
 
  return (
    <View style={styles.container}>
      {/* カスタムヘッダー: ノッチ分の余白を足す */}
      <View style={[styles.header, { paddingTop: insets.top + 8 }]}>
        <Text style={styles.headerTitle}>ホーム</Text>
      </View>
 
      {/* コンテンツ領域 */}
      <View style={styles.content}>{children}</View>
 
      {/* カスタムタブバー: ホームインジケータ分の余白を足す */}
      <View style={[styles.tabBar, { paddingBottom: insets.bottom + 8 }]}>
        <TouchableOpacity style={styles.tab}>
          <Text>Home</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.tab}>
          <Text>Profile</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#fff" },
  header: {
    paddingHorizontal: 16,
    paddingBottom: 12,
    borderBottomWidth: 1,
    borderBottomColor: "#eee",
  },
  headerTitle: { fontSize: 18, fontWeight: "600" },
  content: { flex: 1 },
  tabBar: {
    flexDirection: "row",
    paddingTop: 8,
    paddingHorizontal: 16,
    borderTopWidth: 1,
    borderTopColor: "#eee",
  },
  tab: { flex: 1, alignItems: "center" },
});

期待する動作は、iPhone 15 Pro ではヘッダー上部に約 59pt、タブバー下部に約 34pt の余白が自動で入り、古い iPhone SE では上部に約 20pt、下部に 0pt になります。Android でも同様に端末のステータスバー・ナビゲーションバーの高さが自動で反映されます。

よくあるハマりどころ

① SafeAreaProvider で囲んでいない

useSafeAreaInsets は SafeAreaProvider の子孫でないと全て0を返します。Rork 生成コードでは _layout.tsx や App.tsx にこのプロバイダが入っていないことがあります。その場合は次のように最上位を囲んでください。

// app/_layout.tsx (Expo Router) or App.tsx
import { SafeAreaProvider } from "react-native-safe-area-context";
import { Stack } from "expo-router";
 
export default function RootLayout() {
  return (
    <SafeAreaProvider>
      <Stack />
    </SafeAreaProvider>
  );
}

② 背景色がノッチ部分で変わって見える

SafeAreaView の外側と内側で背景色が異なると、ノッチ周辺で色が切り替わる違和感が出ます。ヘッダーの色を画面全体に見せたい場合は、SafeAreaView ではなく View でルートを作り、中で paddingTop: insets.top を当てる形にすると、ノッチ背後まで色が伸びた一体感のあるデザインになります。

③ ScrollView の中で SafeAreaView を使っている

SafeAreaView は画面全体のラッパーとして使うもので、ScrollView の内側に入れると意図した動きになりません。ScrollView をラップする側に置きましょう。ScrollView のコンテンツが下まで伸びてほしい場合は contentContainerStyle={{ paddingBottom: insets.bottom }} を指定するのが定番のパターンです。

④ モーダル内で insets がゼロになる

iOS のモーダル表示(presentation: "modal")の中でも safe area は必要ですが、モーダル自体の形状(角丸・上部が少し下がる)でノッチは避けられるため、上部 inset は自動で0に近くなります。一方、下部のホームインジケータは残るため、insets.bottom を下部余白として使ってください。

実機での確認手順

Rork での修正は Web プレビューでは検証できないため、実機または Expo Go で確認します。Rork プロジェクトを GitHub にエクスポートし、Expo Go で開くのが最速です。

  1. Rork 画面右上の Export から GitHub へエクスポート
  2. ローカルで npx expo start を実行
  3. iPhone 実機 + Expo Go で QR コードを読み込み
  4. iPhone 15 Pro と iPhone SE、Android 実機の3パターンで見た目を確認

ノッチがある機種と無い機種の両方で崩れないことが確認できれば完了です。Expo Go の Device のメニューから「Device Frame」を切り替えると、複数機種での表示をすぐに比較できるので便利です。

この話題は、iOS と Android で挙動が違う時の原因切り分けガイド や キーボード表示時に入力欄が隠れる問題の直し方 とも密接に関わります。レイアウトの崩れが複数箇所で起きているなら、レイアウトとレスポンシブデザインのトラブルシューティング も合わせて読むと、原因の切り分けがスムーズになります。


まずは今開発中のアプリの最上位 _layout.tsx を開いて、SafeAreaProvider が入っているかだけ確認してみてください。入っていなければ5分で追加でき、それだけで半分以上のレイアウト崩れが消えます。残りの崩れは useSafeAreaInsets で個別に数値を足していけば、実機で見ても違和感のないアプリに仕上がります。

シェア

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

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

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

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

関連記事

⬡ 開発ツール2026-09-10
画面を暗くする機能を足したら、アプリを閉じたあとも iPhone が暗いままでした
expo-brightness の setBrightnessAsync は、Android では現在のアクティビティにだけ効き、iOS では端末の明るさそのものを変えます。戻す責任が片側にしかない理由と、AppState で後始末をするフックの実装をまとめました。
⬡ 開発ツール2026-07-27
最低サポート iOS をいつ上げるか — 利用率ではなく、コードに残った分岐の数で決める
最低サポート OS の引き上げを利用率で判断すると、毎年同じ結論になって先送りが続きます。OS 分岐に注記を付けて棚卸しし、iOS と Android の引き上げ先ごとに消せる分岐を数えるスクリプトと、引き上げ後に見る指標をまとめました。
⬡ 開発ツール2026-05-28
Rork アプリで iOS の BGTaskScheduler.submit が Error Code=1 で失敗するときの切り分け手順
BGTaskScheduler.submit が Error Code=1 を返すとき、原因は 6 種類に収まります。Code=1 と Code=3 の読み分け、Expo 構成に固有の identifier、ウィジェット拡張から submit してしまう落とし穴、getPendingTaskRequests による自己確認まで整理しました。