
iPhone Duo 自适应布局:预留区域、铰链与 Arrangement
这是「iPhone Duo」六支苹果官方 Tech Talks 视频系列的第四篇。前几篇分别讨论设计原则与适配准备,后面的内容还会讲导航栏、多屏和相机。本篇聚焦一个很实际的问题:当屏幕被铰链、摄像头或折叠姿态切开时,现有界面应该移动、缩放,还是换一种布局?
先理解 iPhone Duo 的可用空间
iPhone Duo 有多块显示屏,每块屏幕都有自己的尺寸类。系统还会把铰链,以及内外屏上的摄像头,视为会影响布局的硬件因素。它们形成的区域叫预留区域(reserved region)。
开发时可以把预留区域当成已经存在于布局中的一部分,而不是一块突然出现的黑色空白。系统工具栏和标签栏会在新的安全区域内排列;取景器类界面则要把重要内容和控件放到摄像头区域之外。换句话说,先确保内容确实落在用户能看到、能操作的位置,再考虑怎样填满屏幕。

内屏折叠后,铰链会把显示屏分成多个区域。跨过铰链放一张照片,看起来就像把照片印在书脊上:图像的一部分会变得难以辨认,内容也不再像一个连续画面。应用界面同样如此,跨越铰链的控件可能失去上下文,触控目标也可能落在不适合操作的位置。
位移:先移动已有元素
对于不能自然绕开预留区域的界面,可以使用位移(displacement)模式。它的做法很直接:根据可用空间调整已有元素的 frame,让重要内容在设备折叠后仍然可见、可访问。
位移的范围不必固定。一个独立按钮可以单独移动;一组相互配合的控件则应该一起移动;如果整个区域的关系都需要保持,也可以移动更大的容器。判断标准是元素之间的功能关系,而不是代码层级。能独立调整的元素不要牵连其他内容;必须共同工作的元素不要拆开。

也要控制移动距离。元素离开原来的来源太远,用户就很难把它和原来的内容联系起来。照片选择场景里的上下文菜单就是一个例子:它不会固定在页面底部的正中央,而是随页面移动,并围绕首屏内容对齐。
连续滚动的文章、信息流、文档和列表通常不适合位移。它们本来就能通过滚动适应不同的可用空间,突然在区域之间搬动,会打断阅读的连续性。先判断内容是否已经具备滚动适应能力,再决定是否需要额外处理。
内容该往哪里移动
移动方向由内容的用途决定,最终位置也会随设备姿态变化。
设备像书一样部分折叠时,提示信息等元素可以移动到背面。设备合上后,这些内容会更靠近它原来出现的位置,体验也能继续在外屏上进行。设备立在桌面上时,上方区域适合放需要远距离查看的内容,例如提示信息;下方区域适合放交互控件,例如媒体控制,因为这里更容易形成稳定的触摸表面。

多个区域都能放置内容时,优先保持上下文关联。搜索功能可以像 iPhone 上一样放在键盘上方。设备打开时,它能利用更多横向空间;设备折叠时,宽度和位置一起调整,仍然保持在正在搜索的视图上方。重要的不是固定某个坐标,而是让控件与它服务的内容保持邻近。
适应新的区域
确定移动方向后,还要处理元素进入新区域时的尺寸与视觉属性。位置和大小是最常见的变化,但间距、排列方式等属性也可能需要调整。
操作表、alert、menu 和 popover 都属于轻量的上下文体验。它们的首要目标是完整可见,系统会自动把这些组件重新定位到预留区域周围。使用系统组件时,先依赖这套行为,不要为每种折叠姿态重写一套定位规则。
系统也会调整已有容器。例如类似「提醒事项」的分屏应用,会改变列宽,将可用空间均匀分成两列,让两列都保持可见。网格则可以在铰链附近增加间距,同时保持外边缘不变,使每个项目留在自己的区域内。这个思路比单纯缩小所有项目更可靠:内容、功能和布局关系都要继续成立。
查询预留区域
需要自己布局时,SwiftUI 可以在 GeometryReader 或 onGeometryChange 修饰符中的 GeometryProxy 上查询预留区域。GeometryProxy 的 reservedRegion 方法返回视图中可用的区域,开发者可以据此决定元素的 frame 和间距。
UIKit 则使用 UIView 提供的 reservedRegion 方法,查询返回区域的 frame 属性,并把它纳入自己的布局计算。两套接口解决的是同一个问题:不要猜铰链或摄像头的位置,直接读取当前视图实际面对的预留区域。
预留区域分为活动和非活动两种。默认情况下,reservedRegion 只返回活动区域;需要检查所有区域时,可以使用 includeInactive 查询选项。折叠产生的分割预留区域只有在用户折叠设备时才处于活动状态。设备平放时,它仍然存在,但处于非活动状态,宽度为零。

非活动区域也有用途。比如网格布局可以根据“存在分割区域”这个更高层的事实,提前选择偶数列,而不必只在分割区域已经生效后才改变列数。
另一类是遮挡区域(occlusion region)。它不会把区域切成几块,而是遮住其中一小块,像视野内的一个小框。iPhone Duo 上的 FaceTime 通话摄像头就以遮挡区域表示。把遮挡类型传给 reservedRegion,就能查询这类区域;摄像头启用时区域活动,摄像头停用时区域非活动。
系统容器已经会适应
NavigationStack、NavigationSplitView 和 TabView 是系统提供的导航容器,List 和 ScrollView 是内容容器。它们已经包含一部分适应 iPhone Duo 的行为,所以不要一开始就把所有布局改成自定义计算。
如果界面使用了标准容器,先在不同姿态下检查它的实际表现,再补上真正缺失的部分。这样能避免为系统已经处理好的 alert、popover、列宽或滚动行为重复写代码。
Arrangement:导航和内容之间的一层布局容器
有些界面看起来像分屏,却不需要 NavigationSplitView 自带的展开与折叠功能。Arrangement 是为这类场景准备的布局规则。它位于导航容器和内容容器之间,根据一组规则排列两个视图。
Podcasts 的「正在播放」和文字稿很适合说明这个关系。用户点击按钮显示文字稿时,两个视图会分成两半;iPhone Duo 折叠后,分屏布局可以把它们分别放在铰链两侧,让控制装置保持可操作。设备旋转、变成高度大于宽度时,文字稿又以内联形式显示,而不是硬保留分屏。
一个 arrangement 的输入不只是宽度。系统还要考虑水平和垂直尺寸类、视图宽高比,以及是否存在活动的分割区域。它把这些输入转换成布局输出,包括视图是否显示,以及显示时采用什么 frame。

用 ArrangementView 配置两个视图
SwiftUI 中,可以把 ArrangementView 放进现有的 NavigationStack。它接收 primary 和 secondary 两个视图。以音频笔记应用为例,PlayerView 显示正在播放的音频笔记,UpNextView 显示接下来要播放的笔记。
UIKit 中,UIArrangementViewController 可以作为 UINavigationController 的根视图控制器,再把 Player 和 UpNextView 控制器配置为主要和次要视图控制器。两种实现都把“两个内容如何摆放”交给 arrangement,而不是在每个设备姿态里手动计算。

通过 arrangementViewStyle 可以指定首选排列方式。默认样式是 split,也可以显式指定。
split:优先保持清晰的两块内容
split 会把提供的边界分成 primary 和 secondary 两块。默认情况下,视图宽度大于高度时水平分割;高度大于宽度时垂直分割。这套规则同样适用于 iPad、iPhone 和 iPhone Duo 的宽屏比例。
如果应用只接受水平分割,可以使用 split ArrangementStyle 的 axes 方法指定允许的轴。当前视图的主轴如果不能用于分割,ArrangementView 只显示一个视图。比如设备处于竖向姿态、主轴是垂直方向,但配置只允许水平分割时,系统可以只保留 PlayerView。
UIKit 中,对 UIArrangementViewController 调用 update arrangement,并传入与 SwiftUI 中相同轴配置的 UISplitArrangement。
overlay:有前景和背景关系时叠放
overlay 不倾向于把内容并排分开,而是把内容上下叠放。设备折叠时,它又倾向于把 primary 和 secondary 并排放置,让 secondary 获得更多空间。
使用 overlay 时,可以查询 overlayArrangementZIndex 环境属性。这个值会随设备折叠和展开变化,视图可以据此在折叠版和展开版之间切换。UIKit 中,可以通过 UIArrangementViewController 的 view placement 方法查询主视图的 Z 索引,再读取返回状态的 Z index 属性。

如何选择 split 或 overlay
先看应用现有的布局模式。已经用 HStack 或 VStack 做出类似并排布局的地方,优先考虑 split;已经用 ZStack 做出覆盖层的地方,优先考虑 overlay。这些组件的空间关系已经表达了你的意图,也能自然转成相应配置。
如果没有既有模式,再看两个视图的主次关系。前景控件叠在背景内容上方,且背景内容可以滚动,即使偶尔被遮挡也不影响阅读时,overlay 更合适。辅助功能阅读器就是这种情况:控件在前景,可读内容在背景。
如果两个视图都必须清楚可见,并且 secondary 是对 primary 的补充信息,可以考虑 split。播客文字稿提供当前播客的更多细节,播放器和文字稿都不能被遮挡,因此分屏更合适。音频笔记应用里的 UpNextView 同样属于这种关系。

ArrangementView 不负责导航
ArrangementView 只负责两个视图的布局,不提供应用导航基础设施。因此,不要把 NavigationSplitView 这样的导航容器放进 ArrangementView。导航层应该留在外面,ArrangementView 处理导航之后的内容关系。
同样,不要把 ArrangementView 放进 List 或 ScrollView 等可滚动容器中。滚动容器自身有内容组织和滚动行为,再套一层会让布局职责混在一起,也不符合这套容器的使用方式。
交付前的检查清单
iPhone Duo 的适配可以从现有界面开始,而不是从零重做:
- 检查居中布局在铰链和摄像头出现后是否仍然可读、可操作。
- 对独立控件使用位移;对需要保持关系的一组控件,移动它们共同所在的容器。
- 让提示信息靠近可见区域,让交互控件落在更稳定的触摸区域;连续滚动内容不要随意位移。
- 先验证 NavigationStack、NavigationSplitView、TabView、List 和 ScrollView 的系统行为。
- 对自定义的两栏或覆盖布局,选择 ArrangementView;需要手动避开铰链和摄像头时,再查询
reservedRegion。
自适应布局不是把所有内容都移动一遍。只有当移动、调整大小或重新组织能改善体验时才做。先审查居中布局,再判断该用两列、位移还是 arrangement,通常就能找到改动最少、关系保留得最好的方案。
系列文章
这套教程一共六篇,对应苹果官方的六支 Tech Talks 视频:





































