Highlight
iPadOS 14 的设计目标是为 iPad 带来更多的桌面级生产力体验,同时保留触屏操作的直观性。Session 介绍了 iPadOS 14 中四个核心的 UI 模式更新。
核心内容
iPad 屏幕足够大,但很多 iPad app 仍然沿用 iPhone 的单列导航。用户想在 Mail 里同时看邮箱和邮件,想在 Notes 里一边选择工具一边继续写画,想在 Home 里快速切换房间和设备;旧界面经常让他们在页面之间来回跳转,或者先关掉一个弹出层才能继续操作。
iPadOS 14 给 UIKit 补上了这条路径。UISplitViewController 新增按列组织界面的初始化方式,app 可以直接声明双列或三列布局,再为 primary、supplementary、secondary、compact 等列提供对应的 view controller。UIKit 会按 size class 展示合适的列,并自动提供显示、隐藏列的按钮和手势。
列里的列表也换成了现代做法。Apple 推荐用 UICollectionView 配合 UICollectionLayoutListConfiguration 创建 sidebar 外观,再用 UICollectionView.CellRegistration 和 UICollectionViewDiffableDataSource 把数据接到 cell 上。这样既能得到 iPadOS 14 的 sidebar 样式,也能继续使用 collection view 的拖拽排序、outline、swipe actions 和可定制 cell。
Session 后半段用 Shortcuts 做案例。Shortcuts 在 regular width 里把 tab bar 换成 collapsible sidebar,在 compact width 里仍然使用适合小屏的 tab bar 和列表。它没有维护两套完全割裂的 app,而是把两种导航流程放进同一个 UISplitViewController,再在 size class 切换时恢复用户正在看的 detail 状态。
详细内容
多列布局从 UISplitViewController 开始
(01:57)iOS 14 的 UISplitViewController 新 initializer 接收一个 style,用来声明 app 需要双列还是三列。双列叫 primary 和 secondary,三列会在中间增加 supplementary。
let splitViewController = UISplitViewController(style: .doubleColumn)
关键点:
style: .doubleColumn表示这个 split view 负责一个导航列和一个内容列。- 这个 initializer 会启用 session 中展示的新列管理 API。
- Apple 建议把结构化成列的 app 直接以
UISplitViewController作为 window 的 root view controller。
(02:13)创建 split view 后,把具体 view controller 放进指定列。Home 的例子是 primary 放 sidebar,secondary 放首页内容。
splitViewController.setViewController(sidebarViewController, for: .primary)
splitViewController.setViewController(myHomeViewController, for: .secondary)
关键点:
.primary通常承载导航入口,例如邮箱列表、房间列表或快捷指令分类。.secondary承载用户当前操作的主要内容。- UIKit 会根据 regular width 或 compact width 决定这些列怎样出现。
(02:28)如果 app 的信息层级需要三列,可以把 style 换成 .tripleColumn,再为 supplementary 列提供中间层内容。Mail 的 mailbox、message list、message detail 就是这种模型。
let splitViewController = UISplitViewController(style: .tripleColumn)
splitViewController.setViewController(inboxViewController, for: .supplementary)
关键点:
.tripleColumn适合导航、列表、详情同时存在的生产力 app。.supplementary是 primary 和 secondary 之间的中间列。- 这段 API 只负责结构,列里的具体交互仍由各自 view controller 管理。
compact width 可以使用不同导航流
(04:02)regular width 能显示多列,compact width 没有足够水平空间。Session 展示了 Shortcuts 的做法:在 regular width 使用 sidebar,在 compact width 提供一个 tab bar controller。
splitViewController.setViewController(tabBarController, for: .compact)
关键点:
.compact列用于 iPhone portrait、iPad Slide Over 等窄宽度环境。- compact flow 可以和 regular width 的 sidebar flow 不同。
- 切换逻辑由
UISplitViewController根据 trait collection 处理,app 需要负责保持用户所在位置。
(05:29)多列界面不只是一种固定布局。preferredSplitBehavior 决定按钮和边缘手势在不同 display mode 之间怎样移动。
splitViewController.preferredSplitBehavior = .tile
splitViewController.preferredSplitBehavior = .displace
splitViewController.preferredSplitBehavior = .overlay
关键点:
.tile偏向把列并排摆放,适合希望内容始终可见的 app。.displace会在三列显示时把 secondary 部分推开。.overlay让额外列覆盖在 secondary 上,适合短暂查看导航信息。
(05:56)app 也可以主动请求隐藏或显示某一列。UIKit 会执行平滑过渡,并把自动生成的按钮放到各列 navigation bar 或 toolbar 中。
splitViewController.hideColumn(.primary)
splitViewController.showColumn(.supplementary)
关键点:
hideColumn适合让用户专注当前内容,例如只看文档或邮件正文。showColumn可以把导航列或中间列表带回来。- session 还提到
presentsWithGesture和showsSecondaryOnlyButton,它们控制边缘手势和只显示 secondary 的按钮。
sidebar list 用 UICollectionView 搭起来
(08:06)列只是容器,sidebar 里的列表要用 collection view。iOS 14 提供 UICollectionLayoutListConfiguration,可以直接指定 sidebar 外观。
let configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
关键点:
.sidebar会给 primary 列提供 iPadOS 14 的 sidebar 视觉样式。UICollectionViewCompositionalLayout.list(using:)生成垂直滚动的 list layout。UICollectionView继续保留 collection view 的灵活性,后续可以加 accessories、drag reordering、outline 和 swipe actions。
(09:36)cell 不再需要为了简单列表就写 subclass。CellRegistration 把注册和配置放在一处,并通过 defaultContentConfiguration() 写入文字和图片。
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, MyItem>
{ cell, indexPath, item in
var content = cell.defaultContentConfiguration()
content.text = item.title
content.image = item.image
cell.contentConfiguration = content
}
关键点:
UICollectionViewListCell是 list 场景的标准 cell 类型。MyItem是 app 自己的数据类型,session 示例里包含 title 和 image。defaultContentConfiguration()会按当前 list/sidebar 环境返回合适的默认样式。- 设置
cell.contentConfiguration后,cell 的文本、图片和内边距由系统配置统一管理。
(10:31)最后用 diffable data source 把数据源接到 collection view。它负责根据 section 和 item 生成 cell。
let dataSource = UICollectionViewDiffableDataSource<Section, MyItem>
(collectionView: collectionView)
{ collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
for: indexPath,
item: item)
}
关键点:
Section和MyItem描述列表的数据结构。- closure 在 collection view 需要 cell 时被调用。
dequeueConfiguredReusableCell同时完成复用和配置,避免手动管理 reuse identifier。- session 强调,多数 list 都会遵循 layout、cell registration、diffable data source 这个组合。
(11:29)如果 supplementary 列展示的是内容列表,而不是导航入口,Human Interface Guidelines 建议用 sidebarPlain。
let configuration = UICollectionLayoutListConfiguration(appearance: .sidebarPlain)
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
关键点:
.sidebarPlain适合文件夹项目、集合项目、邮件列表这类内容列表。- session 里提到它会呈现白色背景、分隔线和更偏内容的视觉处理。
- primary column 常用
.sidebar,supplementary column 常见.sidebarPlain。
Shortcuts 案例把结构和状态串起来
(15:35)Shortcuts 的 regular width 版本用 double column split view。左侧 sidebar 负责导航,用户选中项目后,右侧显示 detail。
let splitViewController = UISplitViewController(style: .doubleColumn)
// Primary column
let sidebar = SidebarViewController()
splitViewController.setViewController(sidebar, for: .primary)
// Secondary column
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
splitViewController.showDetailViewController(DetailViewController(), sender: self)
}
关键点:
SidebarViewController放在 primary column。- sidebar 选择事件来自
UICollectionViewDelegate的didSelectItemAt。 showDetailViewController把新的 detail 放入 secondary column。- transcript 说明 Shortcuts 会在 size class 变化时重建当前 detail,避免用户从 split view 切到 compact width 后丢失当前位置。
(20:39)Shortcuts 的 sidebar 也是 collection view。它用 section provider 读取当前 layout environment,在 compact width 下把 header 改成 section 的第一个 item,在 regular width 下不显示 header。
let layout = UICollectionViewCompositionalLayout(sectionProvider: sectionProvider,
configuration: UICollectionViewCompositionalLayoutConfiguration())
func sectionProvider(_ section: Int, environment: NSCollectionLayoutEnvironment)
-> NSCollectionLayoutSection {
var configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
if (environment.traitCollection.horizontalSizeClass == .compact) {
configuration.headerMode = .firstItemInSection
} else {
configuration.headerMode = .none
}
return NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: environment)
}
关键点:
sectionProvider让每个 section 可以根据环境生成不同布局。environment.traitCollection.horizontalSizeClass是判断 compact/regular 的依据。headerMode = .firstItemInSection让 compact width 里仍能表达分组。NSCollectionLayoutSection.list返回真正的 list section。
减少 modality 是交互层面的目标
(12:27)session 还强调,iPad app 应该减少会挡住任务流的 transient UI。Notes 的 color picker 会在用户开始绘制时自动消失,Contacts 的菜单允许用户点按菜单外侧后继续用同一次触摸滚动。这里没有固定 API 配方,Apple 给出的判断标准是观察用户是否已经开始下一步操作,例如滚动、绘制或响应外部事件。
关键点:
- 这是 UIKit 在 iOS 14 中做出的系统级交互改进。
- app 自己也应该观察滚动、绘制、 incoming events 等信号。
- 目标是让用户继续当前任务,而不是先处理关闭弹窗这一步。
核心启发
-
给资料型 app 增加三列阅读模式 做什么:把「分类、列表、详情」同时放在 iPad 横屏界面里。 为什么值得做:10105 展示了
.tripleColumn和 supplementary column,适合邮件、笔记、知识库、文件管理这类信息层级明确的 app。 怎么开始:用UISplitViewController(style: .tripleColumn)建根容器,把分类放.primary,项目列表放.supplementary,详情放.secondary。 -
把旧 tab bar 导航迁移成 collapsible sidebar 做什么:在 regular width 中把底部 tab bar 换成可折叠 sidebar,同时为 compact width 保留 tab bar。 为什么值得做:Shortcuts 通过 sidebar 提供更多导航入口,又用
.compact列保留小屏流程。 怎么开始:为 split view 设置 primary sidebar view controller,再用setViewController(tabBarController, for: .compact)提供窄宽度版本。 -
用 collection view 统一 sidebar、内容列表和 outline 做什么:把 iPad app 里的 table view 列表逐步替换成 collection view list。 为什么值得做:session 明确推荐
UICollectionView作为现代 list 方案,它能继续扩展 accessories、拖拽排序、outline 和 swipe actions。 怎么开始:先用UICollectionLayoutListConfiguration(appearance: .sidebar)搭 primary sidebar,再接CellRegistration和UICollectionViewDiffableDataSource。 -
为内容列选择 sidebarPlain 外观 做什么:区分导航列表和内容列表,让 supplementary column 更像文件、邮件或项目清单。 为什么值得做:Human Interface Guidelines 在 session 中给出的规则是:内容集合或文件夹里的项目使用 sidebar plain。 怎么开始:在 supplementary 列创建 collection view 时使用
UICollectionLayoutListConfiguration(appearance: .sidebarPlain)。 -
主动收起挡住任务流的临时界面 做什么:当用户继续绘制、滚动或处理输入时,自动关闭已经完成使命的 popover、menu 或 transient control。 为什么值得做:session 用 Notes 和 Contacts 说明,多余的 dismiss tap 会打断 iPad 上的连续操作。 怎么开始:从 app 中最常见的 popover 或 menu 开始,找到用户下一步动作信号,在动作发生时关闭临时 UI 并让原动作继续。
关联 Session
- Designed for iPad — 从设计角度讲 sidebar、减少模态、输入方式和自适应布局,是 10105 的设计侧配套。
- Lists in UICollectionView — 深入讲 collection view list 和 sidebar,适合继续拆 10105 中的列表实现。
- Advances in UICollectionView — 讲 list layout、cell registration、现代 cell configuration,是本文代码路径的基础。
- Advances in diffable data sources — 讲 section snapshot、outline、reordering 和 sidebar 数据组织,补齐 sidebar 的数据层。
- Build with iOS pickers, menus and actions — 讲 iOS 14 菜单、picker 和 UIAction,可延伸本文关于减少 modality 的交互改造。
评论
GitHub Issues · utterances