mcpField
I
iphone-duo-react-native

iphone-duo-react-native

github.com/mirzaaghazadeh开发250508 次安装今天更新
代码仓库

关于 iphone-duo-react-native

面向 React Native 开发者的 iPhone Duo 适配指南,介绍无需原生代码的响应式布局与安全区处理,并说明何时通过 Swift 模块接入设备特有功能。

主要功能

  1. 01逐边处理非对称安全区
  2. 02使用窗口尺寸变化触发布局更新
  3. 03按宽度断点切换布局
  4. 04通过原生模块读取折叠区域与铰链角度

适用场景

  1. 01修复折叠或分屏时的边距问题
  2. 02为内屏设计双栏或主从布局
  3. 03在交互确有需要时接入设备特有信息

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mirzaaghazadeh/iphone-duo-skills --skill iphone-duo-react-native

安装后在 Claude Code 中输入/iphone-duo-react-native确认已加载。

详细介绍

iPhone Duo 上的 React Native

React Native 没有折叠或铰链 API。没有 useDisplayFeatures,没有铰链事件,也没有任何能告诉 JavaScript 设备已折叠的机制。截至本文撰写时,还没有任何 React Native 或 Expo 版本支持 iPhone Duo。

好消息是,最常出问题的两件事——尺寸调整和安全区域——已经可以通过你可能已经安装的库来解决。分两个层级处理。

层级 1 — 现阶段可用,无需原生代码

按边处理安全区域内边距

这是 React Native 应用在 iPhone Duo 上出问题的地方,值得明确说明原因:这里的安全区域不对称。 iOS 会将控件移到显示屏的一侧,因此 left 和 right 内边距各不相同,而且具体哪一侧取决于设备姿态,以及应用所在的分屏视图区域。

使用 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 ——它已弃用,而且只有在刘海屏场景下才能正确处理。请使用 context 库的钩子或其 SafeAreaView。
  • 采用对称计算方式 ——任何类似 width - insets.left * 2 的写法,只要内边距不相等就会出错,而在这台设备上大多数时候都是如此。
  • 只读取一次内边距 ——应用运行期间内边距会变化,因此应在每次渲染时通过钩子读取,而不是缓存。

如果你使用的是启用了全面屏显示的 React Native 0.81+,内容已经延伸到系统界面下方,因此正确处理内边距至关重要,而不只是外观问题。

每次渲染时调整尺寸

在 iPhone Duo 上,窗口尺寸会不断变化:打开、合上、折叠、旋转、进入分屏视图都会改变尺寸。

使用 useWindowDimensions()。它是一个钩子,会在尺寸变化时触发重新渲染;而 Dimensions.get('window') 返回的是快照,数据会悄然过时。如果你在模块作用域或常量中使用了 Dimensions.get(...),那么在这台设备上就有问题。

对于组件级尺寸,onLayout 会提供实际测量的布局框,比根据窗口尺寸推算更可靠。

根据宽度判断,而不是根据设备判断

iOS 使用尺寸类别:外屏为紧凑宽度,内屏为常规宽度。React Native 没有对应的概念,因此请使用 useWindowDimensions() 的宽度断点。

不要根据 Platform.OS、react-native-device-info 提供的设备型号或屏幕方向来判断。内屏不遵循受支持的屏幕方向,因此屏幕方向能提供的信息比你想象的少。

为内屏提供真正适用的布局

把手机布局拉伸到 7.6 英寸,会显得很不完整。最简单的改进是在超过宽屏断点后使用双列或主从布局;如果你使用 React Navigation,则可在宽屏上使用抽屉式或侧边栏式导航器,在窄屏上使用标签页。

层级 2 — 需要原生模块

任何真正针对 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 包会在所有 iPhone 上运行,因此对于非折叠设备以及低于 iOS 27.1 的设备,模块都必须返回“无区域、无铰链”,而不是抛出异常。在 Swift 中检查可用性;绝不要想当然。

如果你使用 Expo,这意味着需要配置插件和本地原生模块——它无法在 Expo Go 中运行,因此你需要开发构建。

然后将其接入 Tier 1

区域传到 JS 后,它们就只是现有布局中的数字。将 View 绝对定位在分割区域周围,可以让控件避开折叠处;根据遮挡区域计算 marginRight,可以让内容避开摄像头。

按以下顺序操作

  1. 将所有 Dimensions.get() 替换为 useWindowDimensions()。
  2. 将核心 SafeAreaView 替换为 useSafeAreaInsets(),并检查每个 inset 表达式,确保数学计算对称。
  3. 用宽度断点替代平台/方向分支判断。
  4. 添加宽屏布局路径,让内屏发挥其尺寸优势。
  5. 只有完成以上步骤后,才编写原生模块,并且仅在折叠感知确实能改善应用时才编写。

步骤 1–4 是普通的 React Native 工作,对平板和可折叠 Android 设备也有帮助。步骤 5 是唯一针对 iPhone Duo 的部分,并且需要使用 Xcode 27.1 模拟器进行测试。

原生 API 背后的原理见 ../iphone-duo-adaptive-layout/SKILL.md 和 ../iphone-duo-readiness/SKILL.md。../iphone-duo-design-review/SKILL.md 中的设计指导与框架无关,同样适用于 React Native 应用,无需修改。

评论

支持 Markdown