RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-12中級

Rorkアプリのレイアウト崩れ・レスポンシブ対応トラブルシューティング

Rorkで作ったアプリのレイアウト崩れ、SafeAreaの余白問題、画面サイズ差によるUI破綻を実例コードで解決する実践ガイド

Rork515レイアウトレスポンシブデザイントラブルシューティング77React Native209SafeArea3Flexbox

iPhone 16 Pro Maxで完璧に見えたUIが、iPhone SE3で操作不能になる

Rorkでアプリを作っていて、プレビューでは美しく仕上がったのに実機テストで崩壊する——この経験がある開発者は多いはずです。特に多いのが、大画面のiPhoneで確認して満足した後、小さな画面のデバイスやAndroidで開いた瞬間にボタンが画面外に飛び出す、テキストが重なる、スクロールできないといった症状です。

Rorkが生成するReact Nativeのコードは、基本的にFlexboxベースのレイアウトシステムを使っています。Webブラウザ上のCSSに似ているようで微妙に違うため、Web開発経験者ほど落とし穴にはまりやすい。ここでは私がRorkアプリの開発で実際に遭遇したレイアウト崩れのパターンと、それぞれの修正方法をコード付きで解説します。

SafeAreaViewの罠:ノッチとDynamic Islandに潜む3つの落とし穴

Rorkが生成するコードで最も多いレイアウト問題は、SafeArea周りです。iPhoneのノッチやDynamic Island、Androidのステータスバーとの干渉で、コンテンツが隠れたりパディングが二重にかかったりします。

落とし穴1:SafeAreaViewの二重ラップ

Rorkが生成したコードをカスタマイズする過程で、SafeAreaViewが入れ子になるケースがあります。親コンポーネントと子コンポーネントの両方がSafeAreaViewを使うと、余白が倍になります。

// ❌ 二重SafeAreaView — 余白が2倍になる
const App = () => (
  <SafeAreaView style={{ flex: 1 }}>
    <NavigationContainer>
      <Stack.Screen component={HomeScreen} />
    </NavigationContainer>
  </SafeAreaView>
);
 
const HomeScreen = () => (
  <SafeAreaView style={{ flex: 1 }}> {/* ここで余白がさらに追加される */}
    <Text>コンテンツ</Text>
  </SafeAreaView>
);

修正方法は、SafeAreaViewを最上位の1箇所だけに限定し、子コンポーネントでは通常のViewを使うことです。

// ✅ SafeAreaViewは最上位のみ
const App = () => (
  <SafeAreaView style={{ flex: 1 }}>
    <NavigationContainer>
      <Stack.Screen component={HomeScreen} />
    </NavigationContainer>
  </SafeAreaView>
);
 
const HomeScreen = () => (
  <View style={{ flex: 1 }}> {/* 通常のViewを使う */}
    <Text>コンテンツ</Text>
  </View>
);

落とし穴2:Androidのステータスバー問題

React NativeのSafeAreaViewは、iOSではノッチやホームインジケーターに対応するが、Androidではステータスバーの領域を考慮しない。Rorkで生成したアプリをAndroidで見ると、コンテンツがステータスバーの裏に潜り込む。

// ✅ クロスプラットフォーム対応のSafeArea処理
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
 
// react-native-safe-area-contextを使う方法(推奨)
const Screen = ({ children }) => (
  <SafeAreaView style={styles.container}>
    {children}
  </SafeAreaView>
);
 
// もしくはPlatform判定でAndroidのみ手動パディング
const ScreenManual = ({ children }) => (
  <View style={[
    styles.container,
    Platform.OS === 'android' && {
      paddingTop: StatusBar.currentHeight || 24
    }
  ]}>
    {children}
  </View>
);
 
const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' }
});

ポイントは、React Native本体のSafeAreaViewではなく、react-native-safe-area-contextライブラリのものを使うこと。後者はiOSとAndroidの両方で安全領域を正しく計算します。Rorkのプロジェクトには通常すでにインストールされているが、インポート元が違う点に注意しよう。

落とし穴3:Dynamic IslandデバイスでのSafeArea上端の値

iPhone 14 Pro以降のDynamic Island搭載機では、SafeAreaの上端インセットが従来のノッチ機より大きい(59pt vs 47pt)。固定値でパディングを設定していると、Dynamic Islandモデルでコンテンツが被る。

// ❌ 固定値は特定デバイスでしか機能しない
const Header = () => (
  <View style={{ paddingTop: 47 }}>
    <Text>ヘッダー</Text>
  </View>
);
 
// ✅ useSafeAreaInsetsで動的に取得
import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
const Header = () => {
  const insets = useSafeAreaInsets();
  return (
    <View style={{ paddingTop: insets.top }}>
      <Text>ヘッダー</Text>
    </View>
  );
};

Flexboxの「flex: 1」が効かない——親のheight指定を忘れていないか

Rorkが生成するレイアウトコードはflex: 1を多用します。Flexboxは親要素から子要素へと空間を分配する仕組みなので、親のサイズが確定していないと子のflex: 1は効果がない

典型的な症状は「ScrollViewの中身が見えない」です。

// ❌ ScrollViewの親にflex: 1がない → 高さ0になる
const Screen = () => (
  <View> {/* heightもflexもない */}
    <ScrollView>
      <View style={{ height: 2000 }}>
        <Text>長いコンテンツ</Text>
      </View>
    </ScrollView>
  </View>
);
 
// ✅ 親チェーン全体にflex: 1を貫通させる
const Screen = () => (
  <View style={{ flex: 1 }}> {/* これが必須 */}
    <ScrollView style={{ flex: 1 }}>
      <View style={{ height: 2000 }}>
        <Text>長いコンテンツ</Text>
      </View>
    </ScrollView>
  </View>
);

このルールは「flex: 1のチェーン」と覚えるとよい。ルート(SafeAreaView)から対象のコンポーネントまで、途中の全てのViewにflex: 1が設定されていなければならありません。1箇所でも切れると、そこから下は高さが0として扱われます。

デバッグのコツは、問題のある画面でReact Native Debuggerの「Inspect Element」を開き、各Viewの計算後のheightを確認することです。height: 0のViewがあれば、そこがチェーンの切断点になっています。

固定px指定で壊れる——画面サイズ対応の実践パターン

Rorkでアプリを作ると、AIがwidth: 375のような固定値を生成することがあります。開発中のプレビューでは完璧に見えるが、画面幅が異なるデバイスでは当然崩れます。

パーセンテージの代わりにFlexを使う

React NativeではCSSのwidth: 50%も使えるが、より柔軟なのはFlexboxの比率指定です。

// ❌ 固定幅 — iPhone SE3で画面外にはみ出す
const Card = () => (
  <View style={{ width: 350, padding: 16 }}>
    <Text>カード内容</Text>
  </View>
);
 
// ✅ Flex比率 + 最大幅で制御
const Card = () => (
  <View style={{
    flex: 1,
    maxWidth: 500,
    padding: 16,
    marginHorizontal: 16
  }}>
    <Text>カード内容</Text>
  </View>
);

Dimensionsで画面サイズを取得する

2カラムレイアウトや画像のアスペクト比維持には、画面の実サイズが必要な場面があります。

import { Dimensions, useWindowDimensions } from 'react-native';
 
// ❌ Dimensions.get は初回レンダリング時の値を返す(回転に追従しない)
const { width } = Dimensions.get('window');
 
// ✅ useWindowDimensions は画面回転・分割表示に追従する
const GridItem = () => {
  const { width } = useWindowDimensions();
  const numColumns = width > 768 ? 3 : 2;
  const itemWidth = (width - 48) / numColumns; // 48 = 余白合計
 
  return (
    <View style={{ width: itemWidth, height: itemWidth, margin: 8 }}>
      <Text>アイテム</Text>
    </View>
  );
};

Dimensions.get('window')はアプリ起動時のスナップショットしか返さありません。iPadの画面分割やAndroidのマルチウィンドウで表示領域が変わっても更新されません。useWindowDimensionsフックを使えば、サイズ変更のたびに再レンダリングが走り、常に正しい値が得られます。

ScrollViewとFlatListの落とし穴——「スクロールできない」を解決する

ScrollView内にFlatListを入れてはいけない

Rorkが複雑な画面を生成すると、ScrollViewの中にFlatListが配置されることがあります。この組み合わせはスクロール競合を起こし、パフォーマンス警告が出るだけでなく、FlatListの仮想化(画面外の要素を破棄する最適化)が無効になります。

// ❌ ScrollView内のFlatList — 仮想化が無効になり全データをレンダリングする
const Screen = () => (
  <ScrollView>
    <Text style={styles.header}>ヘッダー</Text>
    <FlatList
      data={items}         // 1000件あると1000件全てレンダリングされる
      renderItem={renderItem}
    />
  </ScrollView>
);
 
// ✅ FlatListのListHeaderComponentを使う
const Screen = () => (
  <FlatList
    data={items}
    renderItem={renderItem}
    ListHeaderComponent={
      <Text style={styles.header}>ヘッダー</Text>
    }
    ListFooterComponent={
      <View style={{ height: 40 }} /> // 下部の余白
    }
  />
);

FlatListのListHeaderComponentListFooterComponentを活用すれば、ヘッダーやフッターをリストと一緒にスクロールさせつつ、仮想化の恩恵も受けられます。

contentContainerStyleを忘れていないか

ScrollViewのスタイリングでよくある間違いが、stylecontentContainerStyleの混同です。

// ❌ style に padding を指定 → スクロールバーの位置がずれる
<ScrollView style={{ padding: 16 }}>
  <Text>内容</Text>
</ScrollView>
 
// ✅ contentContainerStyle でコンテンツ領域にパディング
<ScrollView contentContainerStyle={{ padding: 16 }}>
  <Text>内容</Text>
</ScrollView>

styleはScrollViewの外枠(固定領域)に適用され、contentContainerStyleはスクロールするコンテンツ全体に適用されます。パディングや中央揃えはcontentContainerStyleに書くのが正解です。

テキストが切れる・重なる——フォントサイズと行間の対処法

日本語テキストはアルファベットに比べて文字あたりの面積が大きく、行間も多めに取らないと読みづらい。Rorkが英語ベースで生成したフォント設定では、日本語が窮屈に見えることが多いです。

// ❌ 英語ベースの行間 — 日本語テキストが詰まって見える
const styles = StyleSheet.create({
  body: {
    fontSize: 14,
    lineHeight: 18,  // 行間が狭すぎる
  }
});
 
// ✅ 日本語に最適化した行間
const styles = StyleSheet.create({
  body: {
    fontSize: 14,
    lineHeight: 24,  // fontSize × 1.7前後が日本語に適する
    letterSpacing: 0.3,
  }
});

また、numberOfLinesを設定せずに長いテキストを表示すると、コンテナからはみ出す。切り捨て表示にするなら明示的に指定します。

// ✅ テキスト切り捨ての正しい実装
<Text
  numberOfLines={2}
  ellipsizeMode="tail"
  style={{ fontSize: 14, lineHeight: 22 }}
>
  長い説明テキストがここに入ります。2行を超える部分は省略記号で表示されます。
</Text>

実践:ブレークポイント対応のレスポンシブレイアウトを組む

最後に、Rorkアプリで複数画面サイズに対応するレスポンシブレイアウトの実装例を示す。カスタムフックを1つ作れば、画面全体で一貫したブレークポイント判定ができます。

// hooks/useResponsive.ts
import { useWindowDimensions } from 'react-native';
 
type Breakpoint = 'compact' | 'medium' | 'expanded';
 
export const useResponsive = () => {
  const { width, height } = useWindowDimensions();
 
  const breakpoint: Breakpoint =
    width >= 840 ? 'expanded' :   // iPad・タブレット
    width >= 600 ? 'medium' :     // 大画面スマホ・横向き
    'compact';                     // 通常のスマホ
 
  const isLandscape = width > height;
 
  return { width, height, breakpoint, isLandscape };
};
 
// 使用例:ProductListScreen.tsx
import { useResponsive } from '../hooks/useResponsive';
 
const ProductListScreen = () => {
  const { breakpoint, width } = useResponsive();
 
  const numColumns = breakpoint === 'expanded' ? 4
    : breakpoint === 'medium' ? 3
    : 2;
 
  const itemWidth = (width - (numColumns + 1) * 12) / numColumns;
 
  return (
    <FlatList
      data={products}
      numColumns={numColumns}
      key={numColumns} // numColumns変更時に再マウントするために必須
      renderItem={({ item }) => (
        <View style={{ width: itemWidth, margin: 6 }}>
          <ProductCard product={item} />
        </View>
      )}
    />
  );
};

FlatListnumColumnsを動的に変える場合、keyプロパティにカラム数を渡すのがポイントです。これがないとReact Nativeはカラム数の変更を検知できず、レイアウトが壊れます。公式ドキュメントにも記載されているが、実装時に見落としやすい。

このフックを使えば、「compactではシングルカラム、expandedではサイドバー付き」のような分岐もシンプルに書ける。Rorkが生成した画面に後からレスポンシブ対応を足す場合でも、UIロジックをuseResponsiveに集約することで、各画面のコードを最小限の変更で対応できます。

レスポンシブ対応でお困りの方はRork UXデザインパターン完全ガイドも参考になります。iPadレイアウトに特化した話はRorkでiPad対応アプリを作るガイドで詳しく扱っています。また、スタイリング全般についてはNativeWind(Tailwind CSS)スタイリングガイドが実践的です。

シェア

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

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

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

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

関連記事

開発ツール2026-05-25
Rork で targetSdkVersion 35 にしたら Android のレイアウトが下まで埋まる問題の直し方
Android 15 (API 35) で強制されるエッジ・トゥ・エッジ表示によって、Rork で生成したアプリの下部タブやヘッダーがシステムバーに食い込む問題を、react-native-edge-to-edge と useSafeAreaInsets を使って実装パターン別に解決します。
開発ツール2026-06-12
Rork 製アプリの『書類とデータ』が数 GB に膨らむときの原因と対処 — expo-image のディスクキャッシュ管理
アプリ本体は小さいのに『書類とデータ』だけが数 GB に膨らむ——壁紙アプリの運用で実際に踏んだ expo-image のディスクキャッシュ肥大を、cachePolicy の使い分け・縮小版配信・世代別クリアの三段構えで解決した記録です。
開発ツール2026-05-22
FlatList の onEndReached が連続発火して API を叩きすぎる問題の原因と対処
FlatList の onEndReached が画面遷移時や初期描画時に何度も呼ばれて、ページネーション API を多重に叩いてしまう問題。Rork で生成したリスト画面でも頻発するこの挙動の原因と、実運用で使える対処パターンを整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →