大多数浏览器和
Developer App 均支持流媒体播放。
-
iPad 上的 SwiftUI:添加工具栏、标题及更多功能
利用 SwiftUI 优化您的 iPad App 工具栏。我们将向您介绍如何设计工具栏结构来充分利用 iPad 上的空间,并帮助用户最大限度地提高效率。我们还将了解自定义,探索文档的最新显示方式,等等。该系列讲座包含两个部分,这是第二个讲座。为能更好地理解此视频,我们建议您先观看“iPad 上的 SwiftUI:整理您的界面”。
资源
相关视频
WWDC23
WWDC22
-
下载
♪ ♪
Harry:大家好我是 SwiftUI 团队的工程师 Harry 欢迎来到 SwiftUI on iPad 系列的第二部分 在本系列的第一部分 我的同事 Raj 向您深入介绍了 如何使用 list 和 table 选项和拆分视图 以便让您的 App 在使用更大的 iPad 屏幕 和各种输入设备时表现出众 如果您还没有看过那个课程 我强烈建议您 先从那里开始了解 SwiftUI 我很高兴看到 Raj 构建的 Places App 现在我想给这个程序 添加一些我自己的功能 所以在本次课程中 我会用我最喜欢的工具栏! 在 SwiftUI 中 toolbar API 配置了很多系统栏 类似于 iOS 上的 导航栏或底栏 或者 macOS 上的窗口工具栏 工具栏为您的大部分常用功能 提供了快捷操作 建立好的工具栏能够很好地 提高使用您的程序的用户的工作效率
我花了很多时间思考工具栏组件 以及如何在 Places 中使用 iOS 16 的一些新的工具栏功能 我将首先简要介绍我构建的内容 让您感受一下现在 在 iPad 上可以实现的功能
这是 Places App 在我更新后的样子
您应该会注意到一些新功能 例如前导对齐的导航标题 标题菜单、标题菜单标头 和居中对齐的工具栏项 如果您使用过 Mac 您可能还熟悉 类似自定义工具栏等功能 它允许人们制作 自己的自定义工具栏 这些强大的 Mac 功能 将在 iPad 上首次亮相
在本次课程中 我将展示 toolbar API 的一些增强功能 然后 我将会向您展示一些 关于标题和文档的新 API 那么让我们先从工具栏开始吧 现在 很多人应该已经在自己的 iOS App 中配置了工具栏 并针对更小的屏幕进行了优化 您可能像我第一次看到 Places 一样 添加了菜单
我的菜单代码是这样的
您可以看到我有一个工具栏项 它放在主操作位置 我在这一层内部添加了更多的菜单 并以一些控件作为菜单的选项内容 让我们看看在 iPad 上它是什么样子的 正如您想象的那样 这些菜单栏并没有 充分利用屏幕的可用空间 不过令人开心的是在 iOS 16 中 工具栏可以帮您实现这些类型的菜单 我们称他们为溢出菜单 为了更好的利用这个控件 我将会重组我的工具栏的内容
首先我会将 ToolbarItem (工具栏项) 转换为 ToolbarItemGroup (工具栏项目组) 然后我将删除 Menu 和其内容 然后将原来的内容移动到 工具栏项目组的里面 每个单独的工具栏项 都插入到了这个工具栏项目组中 在 iPad 和 Mac 上 这就是在需要自动将工具栏项 移动到溢出菜单时所需的一切 我可以在这里做更多的事情 但在我做之前 我需要考虑工具栏项的位置 位置定义了工具栏项的呈现区域 不同的呈现位置可以 定义到相同的区域 您可以将导航栏内部 看成三个不同的区域 前导区域、中心区域和尾部区域 前导和尾部区域通常包含一些控件 而中心区域则包含 App 的标题 让我们在 Places 的代码中看看这些区域
在 Places 里 我主要操作的工具栏项目组 放置在 iPad 或 Mac 上的尾部区域 主要操作代表用户在特定屏幕上 使用的最常用操作 在 iOS 16 中 有一个称为次要操作的新位置 这些工具栏项代表的是 不常用的控件 但仍然要保证自己属于工具栏项目 收藏和编辑等操作 在 Places 中是不常用的 所以我会让它们成为次要动作
默认情况下 次要操作在工具栏中不可见 而是隐藏在溢出菜单中 您可以用新的工具栏角色修饰符 来修改工具栏的行为
高亮部分的修饰符通过为工具栏 分配语义角色 来影响它的行为 我在这里传递了一个 编辑器角色 (.editor) 它告诉导航栏应该针对 编辑内容进行优化 导航栏将此解释为工具栏 希望拥有更多空间呈现项目 并将标题从中央区域 移动到前导区域 这在移动溢出菜单之前 为次要操作腾出了中心区域的空间 但在紧凑布局中 导航栏不会改变 并继续在中心区域呈现标题
使用次要操作和工具栏角色 API 让 Places 真正开始利用 iPad 的屏幕尺寸 有了中心区域 我可以在工具栏添加更多项目 比如声音按钮、舒适度按钮 或删除按钮 但是 如果我开始添加很多项目 对某些用户来说 可能会觉得工具栏臃肿而蠢笨 MacOS 长期以来一直支持 自定义工具栏 这让每个人都可以选择 最适合他们的工具栏布局 我很高兴地介绍 现在 iPadOS 也支持用户自定义工具栏 您可以使用现有的 工具栏自定义 API 该 API 已经采用于 macOS 上 现在让我们来看看这个 API 只有工具栏项是可以自定义的 所以首先我需要将我的工具栏项目组 拆分为单独的工具栏项 请注意 这种更改 与之前的效果是一样的 自定义工具栏还需要 将每个工具栏项 与唯一标识符相关联 所以我会为我的 每个工具栏项添加 ID 这些 ID 在 App 启动时 必须是唯一且一致的 这一点很重要 当用户自定义他们的工具栏时 SwiftUI 会保留这些 ID 并使用这些 ID 查找 与其关联的视图以便于呈现 最后 我将给整个工具栏修饰符 添加一个 ID 总之 这将使工具栏支持自定义功能
对于自定义工具栏来说 工具栏项最初不能出现在工具栏中 这些项目在自定义弹出窗口 中开始它们的生命周期 并可在之后添加到工具栏 因为这些项目最初并不存在 所以对特定工作流程中 一些更有用的操作是一个不错的选择 让我们看一下代码
我想隐藏一些当前的工具栏项 以便于新的工具栏项能更容易看到
现在让我们添加一个 包含共享链接的工具栏项 共享链接是 SwiftUI 中的新功能 它依赖于一个称为 transferable 的新协议 要查看有关 Transferable 和 ShareLink 的更多信息 请查看“Meet Transferable”课程 在我的工具栏项到达应有位置后 我将为 showsByDefault 参数赋一个 false 值 这使得该项目一开始 不会出现在工具栏中
现在您可以看到我的共享链接功能 在自定义弹出窗口中展示出来 而且我可以将它 从自定义弹出窗口拖到栏中 用户会喜欢这个功能的 有了我的分享链接之后 我开始思考 工具栏项之间的关系 将分享链接移入工具栏后 我注意到我的图像功能和 地图调整功能是分开的 但我认为这些工具栏项 应该是同一组快速编辑控件 我想在工具栏内部制作这种关系
iOS 16 和 macOS Ventura 支持通过使用控制组 建立这种工具栏项之间的关系 让我来告诉您怎么做 您可以看到我有两个单独的工具栏项 用于图像功能 和地图调整功能的操作 要将它们组合在一起 首先需要将它们移动到同一个项目里 然后我会将它们包含在 同一个控制组中
用户现在可以将它们作为一个单独 工具栏项从工具栏中添加或删除 这很酷 但我可以更进一步 使用 ControlGroup 上 可用的新 API 通过向控制组添加标签 这组工具栏项 可以在进入溢出菜单之前 折叠进自己的小菜单 工具栏开始真正关联在一起了 我想做最后一个改动 添加新地点是一个 常见且重要的操作 所以我想为其添加一个工具栏项
为此 我将在工具栏中 添加一个新按钮 但这次我将使用 主要动作的呈现位置 因为我认为这个新按钮 的功能是常见操作
这个位置凸显了 iOS 和 macOS 之间的一个重要区别 自定义工具栏修饰符中的所有工具栏项 都支持在 macOS 上进行自定义 但在 iPadOS 上 只有次要操作可以自定义 因此 这里的新按钮 呈现在工具栏的尾部区域 并且不能由用户自定义 哇! 关于工具栏的内容非常多 而这些并不是仅有的改进 导航标题也获得了一些 围绕菜单、文档和更多内容的新功能 我们以文档为例 有很多类型的文档 您可能熟悉由文档组 API 管理的文档 文档组 API 具有大量用于 表示和管理文档的内置功能 要说明的是在使用文档组时 所有内容都是免费的
不过 在 Places 中 一个独立的控件也可以认为是一个文档 尽管 Places 没有使用文档组 您可编辑它的属性 也可以在 Places App 中 进行添加或删除操作 也可以和您的朋友分享 一个感兴趣的地点 让我们看看如何在基于 非文档组的 App 中展示这种关系 我已经将 视图的导航标题变为地点名称 所以 我已将一个地点名称的一部分 与我的工具栏的标题相关联 在 iOS 16 中 我可以使用 新的导航标题修饰符 再次进行修改 导航标题栏现在支持显示菜单 您可以把它想象成 macOS 上的文件菜单 要创建菜单 导航标题栏提供一组操作 这些操作您感觉像 创建一个普通的菜单一样 请注意 标题栏现在附加了一个菜单标志 它呈现以您的操作选项构成的菜单 这并不是所有 导航标题都可以做到的 我最喜欢的新功能之一 是它支持可编辑标题 您可以将一个绑定信息 传递给导航标题 这将告诉工具栏 支持编辑标题 您所缺少的只是 一种真正开始编辑的方法 您可以在标题菜单操作中 使用新的 RenameButton 来实现这一点
点击“重命名”按钮可以重命名标题 就像您可以将导航标题 关联到您的视图一样 您现在还可以关联文档 例如我的位置 当提供文档时 标题菜单将呈现 用于显示该文档预览的专用标题 可以拖动预览 并且可以快速访问共享文档 要获取这些标题之一 请使用新的导航文档修饰符 将导航文档与视图关联起来 您可以通过提供符合 transferable 的类型数据 或直接提供 URL 来完成此操作 在这里我将提供一个 URL 它将打开 地图 App 并导航到我正在查看的地方 导航文档修饰符还将 在提供 URL 时 配置 macOS 上的 窗口工具栏的代理图标
最后 我想我应该休息一下 不再更新 App 的工具栏 您能相信所有功能 都是我这一次添加的吗? 我迫不及待地想开始使用它们 在这段示例中 我做了很多事情 来改善 iPad 上的 Places 体验感受 比如溢出菜单和用户自定义功能 使用次要操作放置和自定义 API 真正充分利用 iPad 和 Mac 上更大的可用空间
标题获得了一些在工具栏中 表示呈现自己的新方法 使用导航标题功能创建标题 菜单或支持标题重命名 并记得在适当的时候使用 导航文档修饰符 希望您喜欢 SwiftUI on iPad 系列 随着表格、选择功能 工具栏等方面的所有改进 走出去 让您的 iPad App 更上一层楼 谢谢 祝您度过愉快的 WWDC
-
-
0:01 - Explicit More Menu
PlaceDetailContent(place: $place) .toolbar { ToolbarItem(placement: .primaryAction) { Menu { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label( "More", systemImage: "ellipsis.circle") } } }
-
0:02 - Menu in ToolbarItemGroup
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .primaryAction) { Menu { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("More", systemImage: "ellipsis.circle") } } }
-
0:03 - ToolbarItemGroup with Menu Content
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .primaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } }
-
0:04 - Secondary Action ToolbarItemGroup
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .secondaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } }
-
0:05 - Toolbar Role
PlaceDetailContent(place: $place) .toolbar { ToolbarItemGroup(placement: .secondaryAction) { FavoriteToggle(place: $place) AdjustImageButton(place: $place) AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:06 - Individual ToolbarItems
PlaceDetailContent(place: $place) .toolbar { ToolbarItem(placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(placement: .secondaryAction) { AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:07 - Customizable ToolbarItems
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } } .toolbarRole(.editor)
-
0:08 - ShareLink ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } ToolbarItem(id: "share", placement: .secondaryAction) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:09 - Non-default ShareLink ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { AdjustImageButton(place: $place) } ToolbarItem(id: "map", placement: .secondaryAction) { AdjustMapButton(place: $place) } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:10 - ControlGroup in ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:11 - ControlGroup in ToolbarItem with Label
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("Edits", systemImage: "wand.and.stars") } } } .toolbarRole(.editor)
-
0:12 - NewButton ToolbarItem
PlaceDetailContent(place: $place) .toolbar(id: "place") { ToolbarItem(id: "new", placement: .primaryAction) { NewButton() } ToolbarItem(id: "favorite", placement: .secondaryAction) { FavoriteToggle(place: $place) } ToolbarItem(id: "image", placement: .secondaryAction) { ControlGroup { AdjustImageButton(place: $place) AdjustMapButton(place: $place) } label: { Label("Edits", systemImage: "wand.and.stars") } } ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) { ShareLink(item: place) } } .toolbarRole(.editor)
-
0:13 - Navigation Title
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle(place.name)
-
0:14 - Navigation Title with Menu
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle(place.name) { MyPrintButton() }
-
0:15 - Editable Navigation Title with Menu
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() }
-
0:16 - Editable Navigation Title with RenameButton
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() RenameButton() }
-
0:17 - Navigation Document
PlaceDetailContent(place: $place) // toolbar customizations ... .navigationTitle($place.name) { MyPrintButton() RenameButton() } .navigationDocument(place.url)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。