大多数浏览器和
Developer App 均支持流媒体播放。
-
iPad 上的 SwiftUI:整理您的界面
巧用功能强大的 SwiftUI 列表和表格,使您的 iPad App 告别平凡单调的界面。我们将介绍如何添加选择交互和情景菜单,并帮助您 App 的用户提高效率。我们还将为您提供导航结构设计的最佳实践,探索如何通过分屏浏览避免窗口层叠,从而打造一流的桌面级 iPad 体验。该系列讲座包含两个部分,这是第一个讲座。为能更好地理解此视频,我们建议您先对 SwiftUI 进行一些基本了解。在观看此讲座后,请继续观看“iPad 上的 SwiftUI:添加工具栏、标题及更多功能”,一起来了解如何利用 SwiftUI 来进一步优化 iPad App 的工具栏。
资源
相关视频
WWDC22
- 为您的 SwiftUI App 添加多个窗口
- iPad 上的 SwiftUI:添加工具栏、标题及更多功能
- iPad App 设计的最新更新
- SwiftUI 导航开发指南
- SwiftUI 的新功能
- WWDC22 第 4 天总结
WWDC21
-
下载
♪ ♪
大家好 欢迎收看 “SwiftUI on iPad: Organize your interface” 我是 Raj 从事 SwiftUI 工作 iPadOS 16 有许多更新 可以构建更高效 更专业 功能更丰富的 App 在本次讲座中 我将讨论其中的部分更新 并讨论如何组织 SwiftUI app的界面 使其在 iPad 上大放异彩 首先 我将带你浏览列表和表格 然后 我将讨论 SwiftUI 选择模型 以及如何将选择与菜单集成 最后 我将讨论如何使用分屏浏览 来构建 iPad app 的导航 但请等等 还有更多 这实际上是两部分系列的第一部分 在第二部分 我的同事 Harry 将带你浏览工具栏 标题等 Harry 介绍了一些 非常重要的新增功能 这些功能将 SwiftUI iPad app 提升到了一个新的水平 因此请务必观看这两个讲座 我们先从列表和表格开始 我最近加入了几个读书俱乐部 但我的进展很慢 很难找到一个安静的地方看书 因此 为了帮助我专注于阅读 我开始开发一个 App 来寻找这些神秘的安静之地 安静的地方就像阅读的绿洲 只有沙沙的翻书声 这个 App 可以帮助我跟踪 所有安静的地方 我已经为 iPhone 构建了这款 App 但我认为为 iPad 更新这款 App 会是一个有趣的练习 因为这样可以充分利用更大的屏幕 通过让这款 App 更好地 适用于 iPad 当我最终将其安装到 Mac 上时 还会做进一步的改进 我不会在本讲座中明确介绍 Mac 但所示的许多 API 也适用于 macOS 这是到目前为止我发现的 所有安静地方的清单 这份列表是更新 App 的 一个很好的起点 我已经开始研究 iPad 版本了 这并不可怕 但这并没有利用较大的屏幕 这有点浪费空间 信息密度也很低 幸运的是 在 iPadOS 16 中 有一个很好的解决方案 适用于这些信息密集场景类型 多列表 我来让你们看看它是什么样子的 这是为多列表采用 SwiftUI API 后 “All Places”界面的视图效果 在接下来的几分钟内 我将逐步完成这个截图 SwiftUI 中的多列表格 最早是在 macOS Monterey 中引入的 从 iPadOS 16 开始 同样的表 API 现在也可以用于 iPad 像在 Mac 上一样 iPad 上的表格 支持多列和排序 伴随着 iPad 上表格的引入 SwiftUI 现在在 iPad 和 Mac 上 支持表格分段
上一个讲座 “SwiftUI on the Mac: Build the fundamentals”中 关于表格的一般指导 仍然适用于 iPad 所以如果你还没有看过该讲座 我建议你去看看
我们从 iPhone 列表开始 建立之前显示的表格 这是前面的位置列表的代码
首先 我将从列表切换到表格 表格和列表的构造函数是不同的 表格接受列生成器 而不是视图生成器
我添加的第一列是地名 该列需要一个标题名称 和一个视图生成器 来为集合中的每个元素生成视图 我还指定了一个关键路径 这在以后 向表中添加排序时很重要 请注意 视图生成器 与基于列表的构造非常相似 事实上 我甚至可以重复使用 之前的 PlaceCell 类型
在紧凑尺寸类中 表格只显示它们的第一列 这意味着我的列表在 iPhone 上和 在 iPad 上侧拉时看起来仍然很棒
你可能会注意到 它在外观上与列表有些相似 但我并没有仅仅用列表替换表格 因为重用表格允许 在尺寸类型之间转换时 保留滚动位置和选择 一般而言 确保使用第一栏 以获得紧凑型外观 并始终确保在各种环境中 测试 iPad app 比如 侧拉
好的 我们继续 我将为舒适度和噪声级别添加列 对于只包含文本内容的列 TableColumn 提供了一个方便的 API 允许我在值指向字符串时 省略视图生成器 在这种情况下 我知道舒适度不需要 很大的空间 所以我采用了固定宽度
我还可以使用比较器 将排序添加到表格中 我将创建一些状态来存储比较器 这里的状态是一个数组 因为它代表 表格的所有比较器 并且将初始值设置为名称比较器 可以让表格在第一次显示时 显示排序的结果
接下来 将状态绑定传递到表格中 以便将所有内容连接起来
因为每个列都将其值指定为 可比较字段的 关键路径 所以默认情况下它们是可排序的 现在 这个表格完全可以 按名称 舒适度和噪声进行排序 请注意 表格本身并不处理排序 这取决于我 当排序顺序改变时 我可以使用 onChange 修饰器对数据进行排序
好的 我们来试一试吧
这个表格看起来很棒 显示了所有位置数据 真正利用了大屏幕的优势 与 Mac 不同 iPad 上的表格不会水平滚动 所以限制列数很重要 这确保了所有列 都可以同时显示 每列在头部显示标签 点击标签对该列进行排序 我甚至可以按噪声等级分类
在侧拉功能中 表格折叠成一列 以更简洁的格式表示所有信息 现在我已经将列表更新为一个表格 我们开始选择吧 在本节中 我将回顾 SwiftUI 选择模型 并讨论如何将选择与菜单集成 在此过程中 我将为位置表格增加丰富的功能 但首先 我要谈谈 SwiftUI 中的 选择是如何工作的 SwiftUI 包括一个强大的 API 用于管理列表和表格选择 这是一个图解 里面的列表有多行 每一行都有一个标签 这些标签是每一行的唯一值 用于帮助列表管理所选内容 在这张图表中 标签以绿色圆圈显示
除了标签 还有一些状态保留的选择 这是保存标签值的类型 例如 对于多个选择 这是一个集合 用于保存每个选定行的标签 列表的任务是协调每行中的标签 和选择状态 它通过选择绑定来完成这一步 所以当一行被选中时 比如这里的第二行 列表通过选择绑定将它添加到集合中 同样 如果 App 的其他部分 以编程方式更改了集合 例如添加三个 如图所示 则列表会选择它 因为选择绑定会更改 这个通用模型在 iOS 和 macOS 上 都是一样的 所以有两部分需要选择 标签和状态 接下来我想说一下标签从何而来 标签只是可选容器中视图的一个值 用于跟踪该视图是否被选中 在很多情况下 SwiftUI 可以为你 自动合成标签 标签类似于标识符 但不完全相同 使用 ForEach 时 SwiftUI 将自动 从视图的显式标识符 派生出该视图的标签 而且表格会使用其行值的标识符 作为选择标签 在位置 App 中 这意味着将使用 位置结构的标识符类型 有关显示标识的更多信息 请查看 “Demystify SwiftUI”
要手动为视图做标签 请使用 tag 修饰器 这就是 ForEach 在内部所做的工作 tag 修饰器采用哈希值 但在使用标签修改器时 一定要注意 可选择容器中的所有视图 都共享相同的标签类型 这一点很重要 否则 SwiftUI 可能不知道 如何选择视图 请注意 如果使用 ID 修饰器 它不会设置标签 以上就是标签的介绍 让我们再来回顾一下之前的图解
现在我已经解释了这张图解的标签部分 我想重点介绍选择等式的另一边 选择状态 在上一个示例中我使用了一个集合 但也有其他选项
你可以使用这些数据结构代表选择 SwiftUI 支持单选 macOS Ventura 中新增的 macOS 侧边栏的必选项 以及多选项
iPadOS 16 还引入了轻量级多选 现在 有了键盘连接 你不需要进入编辑模式 选择多行 这有助于避免模态视图 使用键盘时 你可以使用 shift 和 command 等常用快捷键 来扩展和修改选择 这与指针配合使用时效果很好 这是采用选择后位置表格的样子 在本例中 我连接了一个键盘和触摸板 因此行不会缩进 但它们仍处于选中状态 然而 当使用触摸功能时 我仍然需要进入编辑模式 使用双指平移可以加快编辑速度 SwiftUI 自动支持这种手势 说到编辑模式 也有一些 单选和编辑模式的更新 当选择单行时 iPhone 和 iPad 上的列表选择 不再需要编辑模式 这对于协调更新的导航 API 非常有用
通过所有这些更新 我们会得到这个表格 它是在前一个表的基础上 增加了一个编辑模式的新列 只有在没有键盘的情况下 使用多重选择时 才需要编辑模式 因此 我将更新位置表格以支持选择 我可以通过添加一些状态 来存储所选内容 从而轻松地将所选内容 添加到位置表格中 创建状态后 我会向表格的初始值设定项 传递一个绑定 表格强制其选择类型 与其行标识符匹配 因此我使用了位置 ID 类型 作为选择类型 因为我想要多个选择 所以我使用了一个集合作为选择状态 表格会自动为每行做标签 所以我不需要自己做任何标签
现在我可以选择表格中的行了 但我还没有对选中的行做任何事情 我想最好能添加一个按钮 让我可以将选定的地点 添加到指南中 以便与 读书俱乐部中的其他人共享 这是添加工具栏按钮的代码 如果有非空选择 按钮现在就会显示 我还添加了一个编辑按钮 它补充了现有的轻量级选择支持 但在没有键盘的时候 它提供了一种 进入和退出编辑模式的功能 一款出色的 iPad app 无论有无键盘都会大放异彩 因此提供进入和退出编辑模式的控件 非常重要
我们来看看 现在 当我们选择行时 会显示一个按钮 以及一个进入和退出编辑模式的按钮 有关工具栏的更多信息 请务必观看 本讲座的第二部分 我对这里的工具栏按钮很满意 但我们可以做得更多 对于选择操作 尽可能让它们容易访问 是个很好的做法 这就是为什么在 iOS16 iPadOS16 和 macOSVentura 中 SwiftUI 增加了对多选快捷菜单的支持 多选快捷菜单允许 在一组选定的标识符上 显示一个快捷菜单 让我们研究一下这张表格的结构 以了解更多
基于项目的快捷菜单有三种变体 首先 你可以在多个项目上显示菜单 例如顶部的选择
你还可以在单个项目上显示快捷菜单
最后 你可以在 没有内容的空白区域 显示快捷菜单
让我们将这种支持 添加到我们的位置表格中
我省略了前面代码示例中的一些细节 这样我们就可以专注于快捷菜单 我添加了新的 contextMenu 修饰器 该修饰器采用选择类型 这需要匹配列表或表格的选择类型 由于我使用的是表格 我将使用位置 ID 类型
闭包被传递给一组要操作的项 所以若它是空的 我就知道菜单是针对空区域的 我认为一个添加新位置的按钮 对于空白区域来说非常有用 这样 当我在旅途中找到一个 新的安静的地方阅读时 我可以快速添加它 请注意 如果空项目集的视图生成器 无法解析为视图 SwiftUI 将不会在空白区域显示菜单 接下来 我们来处理单个选择 如果该集合只有一个单项 我就知道菜单显示的是单个位置 对于单选和多选 我希望能够将这些位置添加到指南中 因此我将向菜单中添加另一个视图 我们来看看进展 下面是新的快捷菜单支持 点击空白区域会显示一个 添加新位置的菜单项 选择一行仅显示该行的快捷菜单 我可以用键盘扩展选择 创建这个蓝色高亮区域 然后我可以在多行上激活快捷菜单 轻松地将位置添加到指南中
这个表格现在看起来很时髦 是时候在它周围添加一些结构了 为此 我需要一个分屏浏览 导航是 iPad 体验的基础部分 分屏浏览是一个避免在 iPad 更大的 显示屏上出现模态的好方法 它可以一次显示更多信息 无需反复 在本节中 我将介绍 SwiftUI 在 导航和分屏浏览方面的一些更新 在前面的部分中 我创建了位置表格 并增加了丰富的功能 比如选择和编辑模式 但我认为位置 App 缺乏一些结构 所以在本讲座中 我将利用导航分屏浏览 来构建我们 App 结构的基础 SwiftUI 是 iPadOS 16 和 macOS Ventura 中的新功能 它改进了对 NavigationSplitView 类型的 分屏浏览的支持 SwiftUI 支持两列或三列分屏浏览 并具有多种样式 可对各列的显示方式 进行复杂控制 我不会在本期讲座中 全面讲述如何呈现导航内容 为此 我邀请你查看 SwiftUI 导航指南 Curt 有很多秘诀 可以拼凑出超棒的导航体验 而我将更加专注分屏浏览 这张图标显示了 iPad 上的 两列分屏浏览 在 SwiftUI 前导列被称为侧边列 后尾列被称为详细信息列 注意这里的列是如何相互平衡的 在横向 SwiftUI 默认提供此功能 然而 在纵向中 侧边列会隐藏起来 仅显示详细信息列 轻触侧栏按钮 将显示侧边列 该侧边列显示在详细信息列的上方 并使其下方变暗
通常 当空间受限时 两列分频浏览 更倾向于仅展示详细信息列 因为详细信息列经常比侧边列 显示更重要的信息 如果你想自定义此行为 你可以始终优先选择 带有 prominentDetail 导航 分屏浏览样式的 详细信息列或用平衡的 NavigationSplitView 样式 来平衡权重 NavigationSplitView 还支持三列布局 有三列时 在侧边列 和详细信息列中间还有一列 称为 内容列 如果你使用过 UIKit 你或许知道这是补充列 在横向 显示内容和详细信息列 并且可以切换侧边列 点击工具栏按钮后 详细信息列会滑出 为侧边列和内容列腾出空间 纵向仅显示详细信息列 并点击工具栏按钮 显示内容列 从那里 再次点击会显示侧边列 侧边和内容两列都覆盖了详细信息列
一般来说 我推荐 对三列分屏浏览坚持使用 自动样式 因为它可以充分利用可用空间 并且专门用于更大的显示器 就像两列分屏浏览一样 三列分屏浏览可以折叠成 尺寸紧凑类型的 stack 现在我已经讲述了 分屏浏览的基础知识 现在是时候 在位置 App 中添加一个了 这是内容视图 我创建了一个 NavigationSplitView 这里有两列 第一列是侧边列 第二列是详细信息列 详细信息列由侧边列的链接填充 但如果没有显示任何内容 则会显示 select a place 的占位符
这是占位符的屏幕截图 非常棒 这里使用的是自动样式 横向显示侧边列 纵向隐藏 点击侧边列中的某一行 在详细信息列中就会显示该行详情 当使用侧拉时 列会自动折叠 这只是冰山一角 这里还有很多 令人兴奋的导航添加功能 包括对状态恢复的更好的支持 深度链接 甚至更丰富的程式控制 我再次建议你检看导航指南讲座
我已在 App 中构建了 很棒的 iPad 功能 很高兴能找到一些安静的阅读场所 希望我能很快 赶上读书俱乐部的进度 在本期讲座中 我已经介绍了如何利用表格进行 丰富的数据展示 如何管理复杂的选择交互 以及如何利用分屏浏览避免模态
请记得查看相关讲座 并优化你的 SwiftUI app 充分利用 iPad 的强大功能
谢谢大家 ♪ ♪
-
-
3:10 - Places List
struct PlacesList: View { @Binding var modelData: ModelData var body: some View { List(modelData.places) { place in PlaceCell(place) } } }
-
3:18 - Places Table
struct PlacesTable: View { @Binding var modelData: ModelData @State private var sortOrder = [KeyPathComparator(\Place.name)] var body: some View { Table(modelData.places, sortOrder: $sortOrder) { TableColumn("Name", value: \.name) { place in PlaceCell(place) } TableColumn("Comfort Level", value: \.comfortDescription).width(200) TableColumn("Noise", value: \.noiseLevel) { place in NoiseLevelView(level: place.noiseLevel) } } .onChange(of: sortOrder) { modelData.sort(using: $0) } } }
-
10:25 - Places Table with selection
struct PlacesTable: View { @EnvironmentObject var modelData: ModelData @State private var sortOrder = [KeyPathComparator(\Place.name)] @State private var selection: Set<Place.ID> = [] var body: some View { Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { // columns } } }
-
10:26 - Places Table toolbar additions
Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { ... } .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { if !selection.isEmpty { AddToGuideButton(selection) } } ToolbarItemGroup(placement: .navigationBarLeading) { EditButton() } }
-
12:34 - Item context menus
// Item context menus Table(modelData.places, selection: $selection, sortOrder: $sortOrder) { ... } .contextMenu(forSelectionType: Place.ID.self) { items in if items.isEmpty { // Empty area AddPlaceButton() } else { if items.count == 1 { // Single item FavoriteButton(isSet: $modelData.places[items.first!].isFavorite) } // Single and multiple items AddToGuideButton(items) } }
-
16:55 - Navigation Split View example
// Navigation Split View example struct ContentView: View { var body: some View { NavigationSplitView { SidebarView() } detail: { Text("Select a place") } } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。