用途
本技能用于指导开发者将 React Native 或 Expo 应用适配 iPhone Duo——苹果首款折叠屏 iPhone。资料明确指出,React Native 没有折叠或铰链 API,没有 useDisplayFeatures,也没有铰链事件,JavaScript 无法感知设备是否折叠;截至撰写时,尚无任何 React Native 或 Expo 版本正式支持 iPhone Duo。因此适配工作分为两个层级。
主要能力
第一层:无需原生代码,当前即可使用
按边处理安全区域内边距
iPhone Duo 的安全区域是非对称的:iOS 会把控件移到屏幕一侧,因此左右内边距不同,具体哪一侧取决于折叠姿态以及应用占据分屏的哪一半。应使用 react-native-safe-area-context 的 useSafeAreaInsets(),它返回 { top, right, bottom, left } 四个独立数值,并在内边距变化时更新。
const insets = useSafeAreaInsets();
// each edge, independently
paddingLeft: insets.left,
paddingRight: insets.right,
需要避免三件事:
- 使用 react-native 核心的 SafeAreaView——已废弃,且只正确处理过刘海屏场景。应使用上下文库的 hook 或其 SafeAreaView。
- 对称计算——任何形如 width - insets.left * 2 的写法,在左右内边距不同时就是错误的,而在这台设备上这种情况占大多数。
- 只读取一次内边距——它们会在应用运行期间变化,应通过 hook 在每次渲染时读取,而不是缓存。
如果在 React Native 0.81+ 且启用了 edge-to-edge,内容已经延伸到系统 UI 之下,正确的内边距处理就从装饰性变成了关键性。
每次渲染时响应尺寸变化
iPhone Duo 上窗口尺寸会不断变化:打开、合上、折叠、旋转、进入分屏。应使用 useWindowDimensions(),它是 hook,变化时会重新渲染;而 Dimensions.get('window') 返回的是快照,会悄悄过期。如果 Dimensions.get(...) 出现在模块作用域或常量中,在这台设备上就是 bug。对于组件级尺寸,onLayout 能给出实际测量框,比从窗口尺寸推导更可靠。
按宽度分支,而非按设备分支
iOS 以尺寸类思考:外屏是紧凑宽度,内屏是常规宽度。React Native 没有对应概念,因此应使用 useWindowDimensions() 的宽度断点。不要基于 Platform.OS、react-native-device-info 的设备型号或方向来分支。内屏不遵循 supported orientations,方向提供的信息比想象中少。
为内屏提供真正的布局
把手机布局拉伸到 7.6 英寸会显得未完成。低成本的做法是在宽断点以上使用双栏或主从布局;如果使用 React Navigation,宽屏用抽屉或侧栏式导航器,窄屏用标签页。
第二层:需要原生模块
真正 iPhone Duo 特有的功能需要从 Swift 桥接。按以下顺序值得做:
| 原生 API(iOS 27.1) | 为何暴露给 JS |
|---|---|
| UIView 上的 reservedRegions(kind:) | 让触摸目标避开折叠处和屏下摄像头 |
| UIHingeInteraction | 连续铰链角度,用于特效 |
| traitCollection 尺寸类 | iOS 实际使用的信号,替代断点猜测 |
模块形态
编写 TurboModule(新架构)或旧式原生模块,对任何连续数据使用 NativeEventEmitter。铰链角度是实时流——应以事件推送,不要让 JS 轮询。
从 React 内容挂载的视图查询保留区域,因为这些 API 依附于 UIView,区域是相对于某个视图报告的。将区域以普通对象({ x, y, width, height, kind, active })发出,让 JS 能直接据此布局。
严格防护。同一个 JS bundle 会在所有 iPhone 上运行,因此模块在非折叠设备以及低于 iOS 27.1 的系统上必须返回“无区域、无铰链”,而不是抛错。在 Swift 中做可用性检查,绝不假设。
如果在 Expo 中,这意味着配置插件加本地原生模块——无法在 Expo Go 中运行,需要开发构建。
然后接入第一层
一旦区域到达 JS,它们只是现有布局中的数字。围绕分隔区域定位的绝对定位 View 可让控件避开折叠处;从遮挡区域推导的 marginRight 可让内容避开摄像头。
使用方式
按顺序执行:
- 把所有 Dimensions.get() 换成 useWindowDimensions()。
- 把核心 SafeAreaView 换成 useSafeAreaInsets(),并审查每个内边距表达式是否有对称计算。
- 用宽度断点替换平台/方向分支。
- 添加宽布局路径,让内屏物尽其用。
- 只有在此之后才编写原生模块,且仅当折叠感知确实能改善应用时才做。
第 1–4 步是普通 React Native 工作,在平板和可折叠 Android 上也有收益。第 5 步是唯一 iPhone Duo 特有的部分,需要 Xcode 27.1 模拟器来测试。
适用场景
适用于正在维护 React Native 或 Expo 应用、需要为 iPhone Duo 折叠屏设备做适配的开发者。第一层方案对平板和可折叠 Android 同样有益。
限制
- React Native 和 Expo 目前均无正式 iPhone Duo 支持,也无折叠或铰链 API。
- 第二层原生模块需要 Xcode 27.1 模拟器测试,Expo 项目无法在 Expo Go 中运行。
- 原生 API 需要 iOS 27.1 及以上,低版本和非折叠设备必须做降级处理。
- 资料未提供具体收费、价格或平台信息。