Community生產力與協作github.com

expo-duo

用於建置、稽核或修復可在 iPhone Duo(Apple 摺疊式 iPhone)上執行的 Expo / React Native 應用程式,包含垂直邊欄、安全區域內邊距、NativeTabs、SplitView、ArrangementView、expo-hinge、保留區域、方向鎖定,以及在本地或 EAS Workflows CI 中測試 Duo 姿態。與 apple-design-duo(設計規則)和 expo:eas-workflows(CI YAML)搭配使用。

expo-duo 是什麼?

expo-duo 技能是專為在 Apple 摺疊式 iPhone「iPhone Duo」上建置與最佳化 Expo / React Native 應用程式而設計的技術實作層。它涵蓋了從 SafeAreaView 的正確使用、NativeTabs 與 SplitView 等原生導航元件的整合,到進階 API(如 ArrangementView、expo-hinge、react-native-reserved-regions)的應用。此技能支援在本地 Xcode Device Hub 及 EAS Workflows CI 中執行完整測試,確保應用在摺疊、展開、旋轉及分割顯示等所有姿態下都能正常運作。開發者應遵循四條核心規則:讓 iOS 處理導航、每個畫面使用 SafeAreaView(包含水平內邊距)、測試每一種姿態,以及只在必要時使用進階 API 進行微調。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Andrewi89/Stride-skills/tree/HEAD/plugins/design/skills/expo-duo

Installed? Explore more 生產力與協作 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Expo 在 iPhone Duo 上

來源:https://expo.dev/blog/supporting-iphone-duo (Expo SDK 58)。設計規則位於 apple-design-duo;此技能是 Expo 與 React Native 的實作層。CI 管線背景知識:https://docs.expo.dev/tutorial/cicd/introduction/ 及 expo:eas-workflows 技能。

需求

  • Expo SDK 58(目標為 iOS 27)與 Xcode 27.1(iOS 27.1 執行環境加上 iPhone Duo 模擬器)。
  • EAS Build 映像檔:sdk-58 或 latest。
  • Xcode 27 將 Simulator.app 取代為 Device Hub(Xcode > Open Developer Tool > Device Hub)。它有一個可摺疊、旋轉並設定鉸鏈角度的 3D Duo。

心智模型

摺疊 = 緊湊寬度,展開 = 一般寬度。讓版面變寬,但絕不切換設計。雙面保持相同的功能與狀態。在書本般的姿態下,iOS 會將狀態列、分頁列與工具列移至一邊的垂直邊欄中。

四條規則

  1. 讓 iOS 繪製導航列。 使用 Expo Router 的 Stack 與 NativeTabs。自訂的 JS 標題與 <TabList> 不支援邊欄(SDK 58 僅加入最低限度的相容性),而且 <TabList> 無法放入邊欄。
  2. 每個畫面都使用 SafeAreaView(react-native-safe-area-context),包含水平內邊距。
  3. 測試每一種姿態。 摺疊、展開、旋轉、分割顯示。
  4. 只在必要處調整:ArrangementView、preferredPlacement、保留區域、鉸鏈 API,或是停用邊欄。

安全區域(大多數錯誤所在)

  • 邊欄需要左側或右側 84pt 的內邊距。
  • 將捲動內容包在 SafeAreaView edges={['left','right']} 中,而非 ScrollView,這樣背景才能延伸至邊緣。在 ScrollView 上設定 contentInsetAdjustmentBehavior="automatic"(RN patch #59194 應可讓此設定單獨生效)。
  • 水平輪播:自己加入結尾內邊距。
  • 浮動按鈕與提示:right: insets.right + 16。在 RTL 模式下,需同時讀取 left 與 right。
  • 在 NativeTabs 內部,只有當分頁列位於底部時,insets.bottom 才會包含分頁列的高度。絕不要使用固定的分頁列高度。
  • 為內容容器加入內邊距,而非根視圖。
  • 在渲染時讀取 useWindowDimensions。左側分割視窗的寬度為 469pt。

Expo Router

  • 邊欄中的標題按鈕以單欄方式堆疊,返回或關閉按鈕在前。純文字項目保持水平。自訂視圖則被排除。
  • Dynamic Island 中的即時活動可能會將工具列縮小為溢位選單。不要與之對抗。
  • <SplitView> 包裝了系統分割視窗:摺疊時單欄、展開時雙欄,且感知摺疊狀態。在 SDK 58 中為實驗性功能。
  • NativeTabs 的 preferredPlacement:'sidebar' | 'tabBar' | 'automatic'。在橫向的展開顯示器上套用。

額外 API

需求API
雙窗格內容(僅 iOS 27.1,較舊版本會以無容器方式渲染子元素)來自 @expo/ui/swift-ui 的 ArrangementView,置於 Host 內,並使用 ArrangementView.Primary 與 .Secondary。使用 `arrangementViewStyle('split'
摺疊與遮擋框架npx expo install react-native-reserved-regions,然後 useReservedRegions() 會回傳 `{ kind: 'division'
鉸鏈角度與狀態npx expo install expo-hinge。useHinge()、getHinge()、addHingeListener()。狀態:closed、partiallyOpen、fullyOpen、unknown。SwiftUI 修飾符 onHingeChange 可透過 useWorkletCallback 在 UI 執行緒上執行。可與 Reanimated 及 Android 11+ 摺疊裝置搭配使用。
邊欄邊緣useVerticalBarEdgeIOS() 會回傳 'leading'、'trailing' 或 null。並非 Duo 偵測器(展開直向時回傳 null)。
停用邊欄<StatusBar verticalBarBehaviorIOS="disabled" />(對應到 preferredVerticalBarBehavior)。僅用於影片播放器或計算機風格的畫面,且需維持每個畫面的選擇穩定。

方向

在 iOS 27 上,方向鎖定在可調整大小的上下文中是偏好設定。lockAsync 有可能被拒絕,且 requireFullScreen 不再能選擇退出。在 Duo 上鎖定為直向或橫向僅在應用程式隨著姿態變化填滿螢幕時才有效。

已知限制

  • Duo 模擬器中無法使用 StandBy,且 Xcode 27.1 的模擬器中無法使用大多數應用程式擴展。請在實體裝置上測試 Widget 與擴展。
  • expo-app-intents(alpha 版,同樣是 SDK 58)可將動作暴露給 Siri、Shortcuts 與 Spotlight。某些架構所需的 iOS 版本比其所屬領域更高(.mail.openDraft 需要 iOS 27)。與版面配置無關。

測試檢查清單

  • 摺疊: 邊欄下方無重要元素,按鈕可正常回應。
  • 展開: 版面未切換設計,邊欄下方無內容,直向時狀態列回到頂部與底部。
  • 部分展開: 摺疊處無重要元素,工作表與彈出視窗落在單側。
  • 分割顯示: 內容與邊欄保持距離(左側視窗的左邊緣)。
  • 螢幕中間摺疊: 捲動位置、狀態與動作持續保留。
  • 標準 iPhone: 沒有任何改變。

稽核工作流程

  1. 確認 SDK 58 並重新建置。
  2. 搜尋自訂標題或分頁列(headerShown: false 加上自訂列、TabList),並遷移至 Stack 與 NativeTabs。
  3. 搜尋硬編碼的 top、bottom、left、right 偏移量、固定的分頁列高度,以及根層級的內邊距。改用內邊距。
  4. 根據安全區域清單檢查輪播、FAB 與提示。
  5. 選擇雙窗格畫面:SplitView 或 ArrangementView。
  6. 僅在自訂版面確有需要時,決定每個畫面的保留區域或鉸鏈。
  7. 先在 Device Hub 中驗證,然後在 CI 中驗證(如下)。

CI:在 Duo 模擬器上執行

使用 expo:eas-workflows 技能(先取得最新的 schema,不要猜測 job 參數)。一個合理的形狀來自 CI/CD 教學:PR 觸發的模擬器建置,使用 sdk-58 或 latest 映像檔,可選的 Maestro E2E,然後根據指紋在原生建置與用於生產環境的 EAS Update 之間選擇。使用 npx -y eas-cli@latest workflow:validate .eas/workflows/<file>.yml --non-interactive 進行驗證。Workflows 會消耗付費的 EAS 分鐘數。

常見錯誤

錯誤修正方法
為 ScrollView 加入內邊距用 SafeAreaView 包裹,並設定 contentInsetAdjustmentBehavior="automatic"
在內邊距中使用固定的分頁列高度使用安全區域的 insets.bottom
使用 useVerticalBarEdgeIOS() 來偵測 Duo它只代表邊欄邊緣,在展開直向時為 null
使用保留區域在摺疊處分割內容改用 ArrangementView 或 SplitView
自建的 JS 標題或分頁列改用 Stack 與 NativeTabs
浮動按鈕設為 right: 16改為 right: insets.right + 16
假設 lockAsync 總是成功將其視為偏好設定,並讓內容填滿螢幕
只在標準 iPhone 上測試使用 Device Hub,測試每一種姿態

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相關技能