Community生产力与协作github.com

expo-duo

用于构建、审计或修复适用于 iPhone Duo(苹果可折叠 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 Duo(可折叠设备)上构建和优化 Expo / React Native 应用而设计的 AI 技能。它作为 Expo 和 React Native 的实现层,与 `apple-design-duo`(设计规则)和 `expo:eas-workflows`(CI 配置)紧密配合。该技能的核心目标是确保应用在折叠、展开、旋转和分屏等所有姿态下均能正常工作,尤其关注垂直导轨(Rail)的安全区域内边距处理。SKILL.md 文档详细列出了四条规定(如使用 NativeTabs 和 Stack 代替自定义导航栏)以及一系列 API,包括 `ArrangementView`(用于双面板内容)、`expo-hinge`(用于检测铰链角度和状态)、`useReservedRegions`(用于获取折叠/遮挡帧)和 `useVerticalBarEdgeIOS`(用于获取导轨边缘)。它还提供了具体的测试清单和审计工作流,指导开发者如何在本地 Device Hub 和 CI 环境中测试所有姿态。该技能适用于 Expo SDK 58、Xcode 27.1 及配套的 iPhone Duo 模拟器,并强调了使用 `SafeAreaView` 和 `contentInsetAdjustmentBehavior` 避免常见安全区域错误的重要性。

兼容平台~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 on 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 用 Device Hub 取代了 Simulator.app(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. 测试每一种姿态。 折叠、展开、旋转、Split View。
  4. 仅在需要时进行调整:使用 ArrangementView、preferredPlacement、预留区域、铰链 API 或禁用导轨。

安全区域(大多数 bug 的地方)

  • 导轨需要在左侧或右侧有 84pt 的内边距。
  • 将滚动内容包裹在 SafeAreaView edges={['left','right']} 中,而不是 ScrollView 中,这样背景可以边缘到边缘。在 ScrollView 上设置 contentInsetAdjustmentBehavior="automatic"(RN patch #59194 应使此操作足够)。
  • 水平轮播:自行添加末端内边距。
  • 浮动按钮和 toast:right: insets.right + 16。在 RTL 下,同时读取 left 和 right。
  • 在 NativeTabs 内部,只有当标签栏沿着底部运行时,insets.bottom 才包含标签栏。永远不要添加固定的标签栏高度。
  • 使用内边距填充内容容器,而不是根视图。
  • 在渲染时读取 useWindowDimensions。左侧 Split View 窗口宽度为 469pt。

Expo Router

  • 导轨中的头部按钮按一列堆叠,Back 或 Close 按钮在前。纯文本项保持水平。自定义视图被排除在外。
  • 灵动岛中的 Live Activity 可以将工具栏缩小至溢出。不要与之对抗。
  • <SplitView> 包装了系统分屏视图:折叠时一列,展开时两列,可感知折叠。SDK 58 中为实验性功能。
  • NativeTabs 的 preferredPlacement:'sidebar' | 'tabBar' | 'automatic'。应用于横屏状态下打开的显示屏。

额外 API

需求API
双面板内容(仅 iOS 27.1,旧版本将子项渲染为无容器)来自 @expo/ui/swift-ui 的 ArrangementView,位于 Host 内部,搭配 ArrangementView.Primary 和 .Secondary。使用 arrangementViewStyle('split' | 'overlay')、splitArrangementLayoutSize、overlayArrangementEdge 进行样式设置。来自 @expo/ui/swift-ui/modifiers 的修饰符。
折叠和遮挡帧npx expo install react-native-reserved-regions,然后 useReservedRegions() 返回 { kind: 'division' | 'occlusion', frame }(以点为单位)。适用于自定义游戏控件和等列网格。不要在折叠处拆分内容,请使用 ArrangementView。
铰链角度和状态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 上锁定为竖屏或横屏仅当应用通过姿态变化填满屏幕时才有效。

已知限制

  • StandBy 在 Duo 模拟器中不可用,并且大多数应用扩展在 Xcode 27.1 的模拟器中也不可用。请在设备上测试 widget 和扩展。
  • expo-app-intents(alpha 版本,也属于 SDK 58)向 Siri、Shortcuts 和 Spotlight 暴露操作。某些 schema 需要的 iOS 版本高于其域(.mail.openDraft 需要 iOS 27)。与布局无关。

测试清单

  • 折叠态: 导轨下没有重要内容,按钮可响应。
  • 展开态: 布局不会切换设计方案,导轨下没有内容,竖屏状态栏返回到顶部和底部。
  • 部分展开态: 折叠处没有关键内容,sheet 和 popover 位于一侧。
  • Split View: 内容远离导轨(左侧窗口的左边缘)。
  • 屏幕中间折叠: 滚动位置、状态和操作保持不变。
  • 标准 iPhone: 没有任何变化。

审计工作流

  1. 确认 SDK 58 并重新构建。
  2. 搜索自定义头部或标签栏(headerShown: false 加上手工构建的条、TabList),并迁移到 Stack 和 NativeTabs。
  3. 搜索硬编码的 top、bottom、left、right 偏移量、固定的标签栏高度以及根级内边距。替换为内边距。
  4. 根据安全区域列表检查轮播、FAB 和 toast。
  5. 选择双面板屏幕:SplitView 或 ArrangementView。
  6. 如果自定义布局真正需要,再决定每个屏幕是否使用预留区域或铰链。
  7. 在 Device Hub 中验证,然后在 CI 中验证(如下)。

CI:在 Duo 模拟器上运行

使用 expo:eas-workflows 技能(先获取实时 schema,不要猜测 job 参数)。从 CI/CD 教程中得出一个合理的形态:基于 sdk-58 或 latest 镜像的 PR 触发的模拟器构建,可选的 Maestro E2E,然后基于指纹在原生构建和生产环境的 EAS Update 之间做出选择。使用 npx -y eas-cli@latest workflow:validate .eas/workflows/<file>.yml --non-interactive 进行验证。Workflow 消耗付费的 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.

相关技能