97e59b9354e07974c563a0d5e2d3cbec
WWDC20 10105 - 构建全新的 iPad 应用

1. 全文导读

    为了优化 App 在 iPad 上的使用体验,iOS 14 加入了许多 UIKit 新特性。让用户在 iPad 上能够享受更大的屏幕,看到更多的内容。通过减少模态的使用,让用户的操作更加流畅。
    本文首先介绍了如何使用 UISplitViewController 来获得双栏或三栏的各种展示效果,以及如何在不同宽度模式下自由的切换多栏效果以达到最佳的使用体验。
    接着介绍了如何使用 UICollectionView 的新特性来完成多栏中各个页面的开发,并强烈建议还在使用 UITableView 的同学转到 UICollectionView 中来,因为后者更灵活易用,且拓展性更强。
    最后介绍了应用“快捷指令”是如何使用上面的新特性来实现的,让读者有个更直观的感受,从而可以将这些新的特性运用到自己的 App 中去。

2. 几款系统 App 的全新设计

  • 邮箱:使用 UISplitViewController ,双栏、三栏模式自由切换。双栏模式下可以获得更好的邮件阅读体验。三栏模式下能够清晰的看到 MailBox,MailList,Mail 三者的关系,方便用户切换到需要的 MailBox。在 iPad 上充分利用多栏的特性,能够是用户获得良好的使用体验。
  • 备忘录:当我们使用调色板选好的一种颜色,可以使用画笔直接开始画画。不需要像以前那样先点击空白区域让调色板消失再开始画画。整个体验更加的流畅。新版的系统通过减少模态,来减少对用户操作的打断,从而获得更好的体验。
  • 家庭:使用 Sidebar,当移植到 mac 上后,无需额外适配,Sidebar 效果自动获得新版 macOS 设计体验。推荐开发者在自己的 App 上使用 Sidebar。

3. 使用 UIKit 新特性获得更好的iPad体验

3.1. UISplitViewController 实现多栏

    为了获得较好的用户体验,App 在 iPad 和 iPhone 上的布局往往具有差异性。然而是否需要针对 iPad 和 iPhone使用两套代码呢。在本课程,官方特别推荐了使用一套代码编写 iPad 和 iPhone 的应用。在布局思路上,不以机型作为区分,而是使用 Size Classes 的 .regular 和 .compact 模式进行区分。UISpitViewController 的全新 API 为实现此类设计提供了便利。

3.1.1. 模式设置

let splitViewController = UISplitViewController(style: .doubleColumn)   // 双栏模式
let splitViewController = UISplitViewController(style: .tripleColumn)   // 三栏模式

通过新接口设置 .doubleColumn 和 .tripleColumn 可以获得双栏和三栏的 SplitVC。

3.1.2. 为分栏设置视图

splitViewController.setViewController(sidebarViewController, for: .primary)
splitViewController.setViewController(myHomeViewController, for: .secondary)
splitViewController.setViewController(inboxViewController, for: .supplementary)

    通过 setViewController:for: 为每个分栏设置不同的 VC。同时,也可以针对 .compact 模式设置一个视图,当 Size Classes 发生变化,UISplitViewController可以自动切换到相应的模式,以适应不同的屏幕宽度。

splitViewController.setViewController(tabBarController, for: .compact)


通过为 .regular 模式和 .compact 模式设置不同的视图,很快捷的实现了 iPad 和 iPhone 上的不同的样式。同时,当设备分屏,转屏时,系统也能自动完成布局的切换。

3.1.3. 多栏展示模式

    多栏在 iPad 上的展示也是很灵活的,通过点击栏目上的按钮,右滑等手势操作,可以灵活的改变栏目的展示方式。为了实现这样灵活的效果,UISplitViewController在视图结构上做了调整。在 primary, supplementary 和 secondary 视图下面都有一个 UINavigationController。这样就能很方便的在顶部加各种控件按钮,也方便做视图的转场切换。

我们也可以通过代码控制栏目的隐藏和展示。

splitViewController.hideColumn(.primary)    // 隐藏 Primary 视图
splitViewController.showColumn(.supplementary)  // 展示 Supplementary 视图

还可以通过代码设置 SplitVC 的不同样式。下图为紧凑型模式,通过分栏顶部的按钮可以实现分栏的隐藏或显示的切换。

下图为置换型分栏模式,同样是通过点击每个分栏上面的按钮,实现分栏的显示和隐藏

下图为悬浮型分栏模式,通过左滑、右滑手势实现分栏的显示和隐藏

3.1.4. 其他特性

    UISplitViewController 文档详细介绍了其使用的方方面面。开发者如果需要,可以自由设置分栏的宽度,分栏切换的转场动画等效果。同时在设计 SplitVC 布局时,可以参考下官方的设计准则。更多的 UISplitViewController 特性,可以参考官方的文档,链接附在文末。

3.2. 使用 UICollectionView 实现列表

    实现了分栏效果以后,可以为每个栏目填充不同的样式。通常 Primary 和 Supplementary 栏目均为列表样式。我们可以用 UITableView 或 UICollectionView 实现列表样式,但是官方建议使用 UICollectionView 来实现列表。因为 UICollectionView 比 UITableView 更灵活,且具有更好的可拓展性。新的 API 为构建 UICollectionView 提供了更大的便利。
    本内容只简要介绍构造 UICollectionView 的关键步骤,详细的接口文档请参考文末的链接。或者参看 WWDC20 10045 & 10026 -Data Source 和 UICollectionView 在 iOS14 中的新特性

let configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)

    创建如左边图示的 CollectionView,整个配置过程需要三个步骤。从里往外,先是一个 Sidebar 样式的 configuration,接着使用该 configuration 构造 layout,再使用 layout 构造出 CollectionView 视图效果。以上是关于样式的写法。接着要填充真实的 DataSource。

MyItem是每个Cell的真实数据。这里的 CellRegistration 是给每个 Cell 填充对应的数据。具体的填充代码写在图中的代码块内, 每次 Cell 渲染执行代码块中的内容,完成单个 Cell 的绘制。

let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, MyItem> 
{ cell, indexPath, item in
    var content = cell.defaultContentConfiguration()     // 选择cell模式
    content.text = item.title                // 设置cell的标题
    content.image = item.image         // 设置cell的图片内容
    cell.contentConfiguration = content
}

以上是单个cell的 DataSource 填充。整个列表的渲染,由 UICollectionViewDiffableDataSource帮助完成。

```
let dataSource = UICollectionViewDiffableDataSource
(collectionView: collectionView)
{ collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
for: indexPath,
item: item)

top Created with Sketch.