メインコンテンツへスキップ

Capacitor アプリでの iPhone Duo サポート

iPhone Duo向けにCapacitor WebViewアプリを準備する:viewport-fit=cover、env(safe-area-inset-*)、System BarsとStatus Barプラグイン、折りたたみとリサイズのテスト、サイドレールでの@capgo/capacitor-native-navigation。

iPhone Duoを開いて横向きにするとサイドレールにネイティブバーが表示され、縦向きにするとタブバーが下部に表示されます

Appleの折りたたみ式iPhone Duoで、システムクロームとセーフエリアの仕組みが変わります。 Expoが実践的なガイドを公開しました SDK 58上のReact Nativeアプリ向け。この記事はCapacitorの相当版です:1つのWebView、CSS envインセット、Capacitorコアプラグイン、およびオプションのネイティブバーを使用します @capgo/capacitor-native-navigation.

iPhone Duoで変わる点

多くのポーズにおいて、iOSはステータスバー、ナビゲーションバー、タブバーを 縦のサイドレールに リーディングまたはトレーリングのエッジに移動します。横方向のバーは、開いたインナーディスプレイの縦向きや従来のスマホレイアウトでは引き続き表示されます。

上の画像は開いたディスプレイの状態です。横向きではツールバーとタブバーがレールに積み重なります。縦向きではツールバーは上部、タブバーは下部に戻ります。

Capacitor WebView内のWebコンテンツにおいて、重要な変更点は以下の通りです:

  • env(safe-area-inset-top) 〜が可能です。 0 ステータスバーがノッチの下ではなく、レール内にある場合。
  • 先頭と末尾のインセットが増加します (Appleは、横画面や折りたたみ時のレールについて約84ptと記載しています)。左右は互いに鏡像ではありません。
  • ウィンドウサイズの変更 ユーザーが内側ディスプレイを折りたたむ、広げる、回転させる、またはSplit Viewを使うとき。キャッシュされたレイアウト値はすぐに古くなります。
  • ネイティブタブバーはレールに追従します を使用する場合 UITabBar (またはシステムタブバー付きの Capgo Native Navigation)。カスタムの固定HTMLタブバーは追従しません。

折りたたんだ外側ディスプレイは コンパクト幅として扱い、 開いた内側ディスプレイは レギュラー幅として扱います。別のアプリシェルに切り替えるのではなく、レイアウトを広げてください。

iPhone Duoにおけるメールアプリの3つの状態。折りたたみ時は、ツールバーとタブバーが右側の垂直レールにスタックします。Split Viewでは、各ウィンドウは外側の端にレールを保ちます。開いた縦向きでは、ツールバーは上部に沿って配置され、タブバーは下部にピル型で配置されます。

システムバーの配置:デバイスを折りたたんだときは右側のレール、Split Viewでは各ウィンドウの外側の端、開いた縦向きディスプレイでは再び水平バーになります。

要件

  • Capacitor 8 LTS で iOS 27 (iPhone Duo シミュレーターランタイムを備えた Xcode 27.1)
  • Device Hub (Xcode > Open Developer Tool > Device Hub)で、Duo シミュレーターを折りたたみ、ヒンジ操作、回転できます。
  • ある viewport (以下参照)セーフエリアを有効にするもの。

viewport-fit=cover と CSS セーフエリア

セーフエリア環境変数は、viewport がディスプレイ全体をカバーする場合のみ適用されます:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

Pad interactive 各エッジごとのコンテンツ。没入感を出したい場合は、背景をエッジまで広げてください。

選ぶ 1つの アプリ全体で inset の owner を設定します。以下の2つのスニペットは 代替案であり、組み合わせるものではありません。

オプションA: ページごとのシェル

.page {
  min-height: 100dvh;
  padding-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
  padding-bottom: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
  padding-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
}

Option A を使用している場合は、Option B をスキップしてください。

スクロールビューでは、 content外側のスクロールコンテナだけでなく、背景は全幅でスクロールさせつつ、テキストやボタンはレールを避けられます。

使用する 4つの独立した値. 前提としないでください padding-left 等しい padding-right. RTL では、レールはカメラ側に残ります。両方の left と right 環境変数の値を読み取ってください。

見落としやすい2つのポイント:

  • パディング済みのページ内にある水平スクローラーにも、自身の末尾パディングが必要です。そうしないと、最後のカードがレールの下にスライドして入り込みます。
  • floating に設定されたボタンは right: 16px レールを無視します。実際の左または右のインセットを加え、ウィンドウのリサイズ時に再計算してください。

Tailwind を使うアプリでは、 @capgo/tailwind-capacitor 公開し pt-safe, px-safe、関連ユーティリティも提供することで、env() をあちこちで繰り返す必要がなくなります。

Capacitor System Bars (Android のセーフエリアと iOS の設定)

Capacitor 8 がリリース システムバー における @capacitor/core。これは従来の Android 専用の adjustMarginsForEdgeToEdge フラグを置き換えます。設定は次のとおりです。 capacitor.config.ts:

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  plugins: {
    SystemBars: {
      insetsHandling: 'css',
    },
  },
};

export default config;
  • insetsHandling: 'css' (Android ではデフォルト): 注入するのは --safe-area-inset-* WebView の env 値が正しくない場合 (古い Android WebView ビルド) の CSS 変数です。
  • オプションB(サイト全体のシェル)は フォールバックパターン を System Bars ドキュメント。オプションBを使用する場合は、オプションAをスキップしてください。
html {
  padding-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
  padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
  padding-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  padding-bottom: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
}

実行時に、以下でバーをスタイル設定したり非表示にしたりできます SystemBars.setStyle, SystemBars.showや SystemBars.hide と @capacitor/core。別の @capacitor/safe-area パッケージはありません。Capacitor アプリのセーフエリアは CSS env, Androidのシステムバー変数、および resize / visualViewport.

@capacitor/status-bar

この ステータスバープラグイン iOSでは引き続きオーバーレイとスタイルを制御します:

import { StatusBar, Style } from '@capacitor/status-bar';

await StatusBar.setOverlaysWebView({ overlay: true });
await StatusBar.setStyle({ style: Style.Dark });

setOverlaysWebView({ overlay: true }) HTMLがステータスバー領域の下に描画できるようにします。envパディングと組み合わせないと、コンテンツがクリップされます。iPhone Duoではオーバーレイモードが引き続き適用され、 ジオメトリ のインセットが上か側面かは、ステータスバーAPIだけでなくシステムのセーフエリアから決定されます。

スタックしないでください WebViewの自動マージン, 全ページenvパディングや ネイティブバーのインセット変数 明示的な戦略がない場合、このパターンはすべてのiPhoneで二重の余白を引き起こし、レールがインセットを左右に移すとさらに悪化します。

iOS WebViewのcontentInset

では capacitor.config.ts, ios.contentInset WebViewのスクロールビューの〜にマッピングされます contentInsetAdjustmentBehavior。多くのアプリは〜を使用しています 'never' CSSでインセットを処理する場合。UIKitの自動調整に頼っているなら、 と CSS env padding を使い、信頼できる情報源を1つに絞ってください。

詳細は Capacitor iOS設定.

折りたたみ・展開時のWebViewリサイズ

Capacitor はフルスクリーンの WebView を1つに保ちます。Duo のポーズが変わると:

  1. UIKit がビューコントローラーのセーフエリアインセットを更新します。
  2. 新しい幅と高さで WebView のレイアウトパスが実行されます。
  3. CSS env(safe-area-inset-*) と 100dvh / visualViewport 次のスタイルフラッシュ時に値が更新されます。

JavaScriptでジオメトリ変更を監視する インセット依存の位置をキャッシュする場合(フローティングボタン、トースト、Canvasオーバーレイなど):

function onGeometryChange() {
  const vv = window.visualViewport;
  const width = vv?.width ?? window.innerWidth;
  const height = vv?.height ?? window.innerHeight;
  const offsetLeft = vv?.offsetLeft ?? 0;
  const offsetTop = vv?.offsetTop ?? 0;
  document.documentElement.style.setProperty('--app-vv-width', `${width}px`);
  document.documentElement.style.setProperty('--app-vv-height', `${height}px`);
  document.documentElement.style.setProperty('--app-vv-offset-left', `${offsetLeft}px`);
  document.documentElement.style.setProperty('--app-vv-offset-top', `${offsetTop}px`);
}

window.addEventListener('resize', onGeometryChange);
window.visualViewport?.addEventListener('resize', onGeometryChange);
window.visualViewport?.addEventListener('scroll', onGeometryChange);
onGeometryChange();

スクロールリスナーは、visual-viewportのパンによる offsetLeft / offsetTop 幅や高さを変更せずに視覚的ビューポートが変化するケース(iOSでのキーボード表示によるビューポートシフトなど)をカバーします。

Androidでは、メインアクティビティに density における android:configChanges を含めることで、折りたたみ式端末やディスプレイサイズ変更時にWebViewが破棄されないようにします。Capacitor の8つのテンプレートがこの設定を記載しています。 Capacitor 8 アップグレードガイド.

画面の向きとウィンドウサイズクラス

使用する ライブ レイアウトの分岐に幅を screen.width 起動時に一度だけ取得されます:

const compactQuery = window.matchMedia('(max-width: 600px)');

function readCompactWidth() {
  return compactQuery.matches;
}

compactQuery.addEventListener('change', () => {
  document.documentElement.dataset.widthClass = readCompactWidth() ? 'compact' : 'regular';
});
document.documentElement.dataset.widthClass = readCompactWidth() ? 'compact' : 'regular';

CSSでは、 minmax() 固定値より 100vw 幅。 100vw スクロールバーやレールの非対称性を無視し、横方向のオーバーフローを引き起こすことがよくあります。

内側ディスプレイでのSplit Viewでは、各ウィンドウが狭くなることがあります(Appleは約469pt幅と記載しています)。アプリを左右にドッキングした状態でテストしてください。レールは ウィンドウの 端に付くため、ウィンドウ間で水平インセットの左右が入れ替わることがあります。

カスタムHTMLのクローム vs ネイティブバー

Expoのガイドではネイティブ実装を推奨しており、 Stack と NativeTabs iOSがレールの配置を管理します。Capacitorでも同じ考え方が当てはまります:

アプローチ iPhone Duoの挙動
固定HTMLヘッダー + ボトムタブ 従来の上下の安全領域内に留まります。レール側には移動しません。
@capgo/capacitor-native-navigation システムと UITabBar / UINavigationBar バーはシステム配置に従い、縦レールも含みます。

HTMLのUI要素を維持する必要がある場合、すべての画面で 左右の env padding を確認してください。上下だけでなく。

固定HTMLヘッダーと下部タブ付きの同じ画面。折りたたみ時は、ヘッダーとタブは従来の矩形内に留まり、レールを回避するだけです。縦長に開いた場合、上部と下部に留まります。

固定された HTML の chrome はレールに追従しません。上下のセーフエリア内に留まります。UIKit や @capgo/capacitor-native-navigation によって描画されるネイティブバーは、システムに合わせて移動します。

iPhone Duo での @capgo/capacitor-native-navigation

ネイティブナビゲーションは実際の UINavigationBar と UITabBar (または Android では Material バー)を 1 つの Capacitor WebView に重ねて描画します。iOS 27 の iPhone Duo では、 折りたたみ時や多くの横画面姿勢で、システムタブバーがサイドレールに移動します。プラグインの 折りたたみ時や多くの横画面姿勢で、システムタブバーがサイドレールに移動します。プラグインの 曲線に沿います タブバーの形は下端に沿ったまま。ウィンドウが広がる間も、中央のアクションは円形のクレードルを維持します。

@capgo/capacitor-native-navigation. の2つの横向きレイアウト。左ではシステムタブバーがサイドレールにあり、右ではカーブしたタブバーが下端に沿って配置され、中央のアクションが円形のクレードルに収まります。

左:システムタブバーはレール内。右:カーブしたタブバーは下端に沿い、中央のアクションはクレードルを維持。

一度設定するだけ

import { NativeNavigation } from '@capgo/capacitor-native-navigation';

await NativeNavigation.configure({
  contentInsetMode: 'css',
});

contentInsetMode: 'css' バーとセーフオーバーラップのサイズをドキュメントルートに書き込みます:

  • --cap-native-navigation-top
  • --cap-native-navigation-right
  • --cap-native-navigation-bottom
  • --cap-native-navigation-left
  • --cap-native-navbar-height
  • --cap-native-tabbar-height
  • --cap-native-system-tabbar-height (〜のときの標準タブバー高さ tabbarHeight が 0 (ホストされたシステムバー用)

Native Navigation がバーを管理している場合は、以下のプラグイン変数でパディングしてください。バーがアクティブなときは、ネイティブバーの重なりと UIKit のセーフエリア端がすでに含まれています。次を使用します: max() 変数が〜のときもエッジが正しく保たれるよう、env フォールバック付きで 0 (例:ホストされたシステムタブバー) を〜なしで 追加 プラグインと env のパディングを一緒に使用しないでください。

main {
  padding-top: max(var(--cap-native-navigation-top, 0px), env(safe-area-inset-top, 0px));
  padding-right: max(var(--cap-native-navigation-right, 0px), env(safe-area-inset-right, 0px));
  padding-bottom: max(var(--cap-native-navigation-bottom, 0px), env(safe-area-inset-bottom, 0px));
  padding-left: max(var(--cap-native-navigation-left, 0px), env(safe-area-inset-left, 0px));
}

同じ要素にオプション A またはオプション B のパディングも適用しないでください。 main 。

iOS 26 以降で system-hosted タブバー、 tabbarHeight プラグインのインセットは 0 Webコンテンツが2回持ち上げられないようにします。 --cap-native-system-tabbar-height レール上部のフローティングアクションに余白が必要な場合に使用します。

safeAreaChanged

折りたたみ、レール側、バーの表示状態が変更されたときに購読します:

await NativeNavigation.addListener('safeAreaChanged', ({ insets }) => {
  console.log(insets.top, insets.right, insets.bottom, insets.left);
  console.log(insets.navbarHeight, insets.tabbarHeight, insets.systemTabbarHeight);
});

プラグインはまた capNativeNavigation:safeAreaChanged で window 同じ insets ペイロード。

Native code は小さな 上部の隙間を safeInsets.top が0のとき (レール内のステータスバー)に隙間を設けることで、ナビゲーションバーのボタンが角丸ディスプレイの角に触れないようにします。Web 側のパディングでは、レール用に引き続き env left/right を使用してください。

setNavbar, setTabbarや configure すべて返します { insets } chrome を更新した後に一度だけ読み取りたいとき。

API の完全リファレンス: Native Navigation ドキュメント.

テスト用チェックリスト

iPhone Duo シミュレータで Device Hub を使用する場合:

  1. 折りたたみ: コントロールはレールの下にありません。タップは登録されます。水平方向の env insets は、期待される場所でゼロではありません。
  2. 縦向きで開く:トップ/ボトムバーはiOSが配置する位置に戻り、レイアウトが別の情報設計に切り替わることはありません。
  3. 横向き / レールで開く:コンテンツはサイドレールを避けます。フローティングアクションは insets.right または insets.leftを使用し、ハードコードされた 16px.
  4. 部分的な折りたたみ:ヒンジ部分に重要なUIを配置しない。モーダルやシートは引き続き使用可能。
  5. スプリットビュー:左右のウィンドウでアプリを実行。レールの端は各ウィンドウに追従します。
  6. 画面表示中に折りたたむ:ルート状態、スクロール位置、フォーム入力は遷移後も保持されます。
  7. クラシック iPhone シミュレータ:リグレッションなし。

Agentプロンプト(コピー&ペースト)

このブロックを、Capacitor リポジトリで作業している Claude Code、Cursor、Codex、その他のコーディングエージェントで使用してください:

Task: Make this Capacitor app safe for iPhone Duo (iOS 27 foldable).

Context:
- Read https://capgo.app/blog/capacitor-iphone-duo-safe-area-guide/ and Expo's https://expo.dev/blog/supporting-iphone-duo for platform behavior.
- One WebView. Use viewport-fit=cover and per-edge env(safe-area-inset-*) padding for interactive content; backgrounds may ignore safe areas.
- Capacitor 8: configure plugins.SystemBars (insetsHandling css). Set viewport-fit=cover in index.html. Use var(--safe-area-inset-*, env(...)) fallbacks on Android.
- @capacitor/status-bar: only if the app already uses it; setOverlaysWebView must match CSS padding strategy.
- Do not double-apply Capacitor margin adjustment, full-page env padding, and native navigation CSS variables.
- On resize and visualViewport resize, recompute any cached inset or FAB positions.
- If the app uses @capgo/capacitor-native-navigation: contentInsetMode css, listen for safeAreaChanged, pad with max(var(--cap-native-navigation-*, 0px), env(safe-area-inset-*, 0px)) per edge (not sum); verify system tab bar rail poses in the iOS 27 Duo simulator.
- If the app uses custom HTML headers or tab bars, add left/right safe padding on every screen and document that bars will not move into the rail.

Deliverables:
1. List files changed and the inset strategy (single source of truth).
2. Replace fixed top-only safe padding with four-edge padding where needed.
3. Add or update capacitor.config and index.html viewport meta if missing.
4. Note manual test poses: folded, open portrait, open landscape rail, Split View left/right.

Constraints:
- Only use APIs that exist in @capacitor/core, @capacitor/status-bar, and @capgo/capacitor-native-navigation definitions.
- No em dashes or en dashes in user-facing strings you add.

インストール Capgo スキル エージェントが Native Navigation を接続する方法がわかるようにします: npx skills add Cap-go/capgo-skills --skill capacitor-plugins.

参考資料

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。