大多数浏览器和
Developer App 均支持流媒体播放。
-
提升 iPadOS 中的标签页和边栏使用体验
iPadOS 18 引入了一种新的导航机制,让用户能够灵活选择使用标签栏还是边栏。设计一新的标签栏可为内容和其他功能提供更多空间。了解如何使用 SwiftUI 和 UIKit 实现添加、移除及重新排列标签页等自定功能,让 App 更显个性化。
章节
- 0:00 - Introduction
- 0:52 - Tab bar and sidebar refresh
- 3:56 - Tab bar and sidebar features
- 4:28 - Tab bar SwiftUI updates
- 5:00 - Tab bar UIKit updates
- 5:58 - Search tab
- 6:41 - Enable sidebar with TabView in SwiftUI
- 7:16 - Enable sidebar with UITabBarController in UIKit
- 7:46 - Sidebar actions
- 8:13 - Drop destinations on Tabs in SwiftUI
- 8:25 - Drop destinations on UITabs in UIKit
- 9:15 - User customization
- 10:45 - Enable customization in SwiftUI
- 12:38 - Enable customization in UIKit
- 13:52 - Platform considerations
资源
- Destination Video
- Elevating your iPad app with a tab bar and sidebar
- Enhancing your app’s content with tab navigation
- Forum: UI Frameworks
相关视频
WWDC22
WWDC20
-
下载
大家好 欢迎观看“提升 iPadOS 中的标签页和边栏使用体验” 我是 UIKit 工程师 Andy 在这个视频中 我将介绍 标签页栏和边栏增强功能 它们可提升 App 使用体验 并帮助你将内容置于显眼位置
自推出以来 标签页栏已成为 iPhone 和 iPad 的 一种核心导航模式 标签页会将你的 App 内容 分为不同的部分
标签页栏让用户 能在标签页之间快速切换 同时保留每个标签页的当前状态 例如 在“时钟”App 中 标签页栏中有四个不同的标签页 “世界时钟”、“闹钟” “秒表”和“计时器”
在 iPadOS 18 中 标签页栏以全新的紧凑外观设计亮相 减少了未使用的 垂直空间和水平空间 让更多内容出现在显眼位置
现在 标签页栏位于 App 的顶端 这里更靠近其他导航控件 方便用户使用 此外 标签页栏还与顶栏共享空间 这让 App 能够显示更多内容
标签页应在 iPhone 和 iPad 之间保持一致 这样一来 无论用户使用 哪台设备都能轻松导航 请勿添加过多标签页 过多的选择会让用户眼花缭乱 难以在 App 中找到所需信息
与标签页栏一样 边栏的作用 也是为了方便用户在 App 中导航 让用户能够快速访问顶层目标位置 边栏可以显示顶层内容集合 例如 文件夹或播放列表 让导航更高效 在 iPadOS 17 及更早版本中 边栏通过分屏浏览的形式呈现 其中前导列中会显示大纲视图
而在 iPadOS 18 新版本中 你可以将标签页栏显示为边栏 并通过几项增强功能大幅改善 边栏效果
隐藏边栏后 它会转换为标签页栏形式 让用户的焦点重新回到标签页内容上
用户可以使用标签页栏进行导航 无需重新打开边栏
标签页栏会显示 App 的顶层部分 而边栏还可以显示同一个 App 层次结构中的更多部分 标签页栏和边栏 非常适合具有丰富层次结构 且聚焦于内容的 App
要了解标签页栏导航的最佳做法 欢迎观看“探索 iOS 的导航设计” 要详细了解边栏 欢迎观看 “专为 iPad 设计”
内容丰富的 App 不仅应该功能强大 还应该具有个性化特点 新版边栏支持自定功能 便于用户显示或隐藏单个标签页 或对标签页进行重新排序
标签页栏方便用户通过拖放的方式 添加常用标签页 从而轻松打造个性化体验
搭配使用标签页栏与现有的边栏 可以同时享受这两项功能的优点 并能通过新的方式 自定和个性化你的 App 首先 我将介绍新版标签页栏和 边栏的功能 以及使用时的注意事项
接下来 我将介绍如何 通过支持用户自定功能 为 App 增添更多个性化的元素
最后 我将介绍这些功能 在不同平台上的效果 以及构建多平台体验时的 注意事项
首先 我来介绍一下标签页栏和边栏 使用 iPadOS 18 SDK 进行编译时 现有的标签页栏 App 将更新为 新界面 无需更改任何代码 在新界面上 标签页栏与导航栏共享 一个安全区域 界面更紧凑
如果标签页栏旁边没有足够的空间 则导航栏中的工具栏项 会自动转移到溢出菜单
在 iOS 18 的新功能中 SwiftUI 中的 TabView 采用了一种新语法 方便我们在构建时捕捉常见错误 现在 你声明了一个 Tab 结构 其中包含标题、图像及内容视图
你还可以 在 Tab 中包含选择值 以启用编程选择功能 新语法可确保所有标签页 都具有相同的选择类型 并且类型与 TabView 本身相匹配
在 iOS 18 的新功能中 UIKit 新增了 API 以便更好地向 UITabBarController 描述 App 层次结构
创建 UITab 来代表 App 的 每个顶层部分 并将它们分配给 tabBarController 的 tabs 属性 对 UITab 实例所做的更改会立即 反映在标签页显示位置
标签页栏倾向于使用实心图形 而边栏则倾向于使用轮廓图形 针对标签页应用符号图像时 请使用轮廓图形
如果图像显示在标签页栏 系统将自动选择实心图形 例如 在“音乐”App 中 “浏览”标签页使用的 square.grid.2x2 符号 就是一个轮廓图形 在标签页栏中 会自动显示实心图形 无需另行指定图像
搜索是所有 App 的常用功能 用户一眼就能找到 它专用的放大镜符号
使用 SwiftUI 中的搜索功能 Tab 或 UIKit 中的 UISearchTab 系统将配置一个具有默认标题、 图像和固定位置的标签页 固定位置意味着 这个标签页会一直出现在 标签页栏的后缘
iPad App 中的边栏非常实用 层次结构丰富 可显示更多内容集合
利用新的 Tab 和 UITab API App 能够同时表示 App 中 标签页栏和边栏的结构
要通过 SwiftUI 中的 TabView 启用边栏 首先 请将 tabViewStyle 设置为 sidebarAdaptable
然后 使用 TabSection 表示边栏中的一个标签页组
标签页会按照声明的顺序 出现在标签页栏中 在边栏中 各个部分 会排在单个标签页之后 在这个示例中 边栏中的搜索标签页会显示在 两个 TabSection 的前面
要通过 UIKit 中的 UITabBarController 启用边栏 请将 tabBarController 的模式 设置为 tabSidebar
与 TabSection 类似 使用 UITabGroup 来表示属于 单个标签页组的 子标签页集合
包含动态内容的标签页组会通过 直接更改标签页组的子项来进行更新
你还可以针对边栏的 各个部分添加操作 从而为常见任务提供便利 比如在“播客”中创建新的电台
标签页同时也是拖放的目标位置 这样就可以通过拖放的方式 直接将内容添加到 边栏或标签页栏中 比如 将照片添加到精选集中
要在 SwiftUI 中的 Tab 上 支持拖放目标位置 请使用 Tab 上的 dropDestination 修饰符并选择接收器类型
要在 UIKit 中的 UITab 上 支持拖放目标位置 可通过 UITabBarControllerDelegate 使用两种方法实现 首先 如果能够接收拖放 则从 operationForAcceptingItemsFromDropSession 返回有效的拖放操作
接下来是接收数据 需要从 acceptItemsFromDropSession 的拖放会话中加载数据
除了操作和拖放目标位置之外 还有很多新的 API 可用于自定边栏 你可以自定边栏的页眉和页脚 也可以向标签页添加 轻扫操作或上下文菜单 此外 你还可以显示 标签页的弹出窗口 并且弹出窗口会锚定在 标签页显示的任何位置 借助标签页来描述 App 只是第一步 接下来 我将介绍如何添加用户自定 让你的 App 更显个性化
启用自定功能 可让用户根据特定需求调整边栏 用户希望隐藏不必要的标签页 或重新排列边栏中的标签页组 针对顺序和可见性进行的自定 会自动长久保存
标签页栏自定功能 与 iPadOS 16 中引入的 工具栏自定功能类似
标签页栏中的各项 可通过拖放进行自定 既可从边栏中添加也可将它们拖走
标签页有一个首选的放置方式 用于确定它的自定和 可见性偏好设置
标签页栏包含三个部分
固定不变部分针对 App 的 关键目标位置而设计 它们显示在其他项之前 并且不允许自定
可自定部分中的项 可以重新排序 用户也可以从边栏进行拖放 来添加其他项
固定不动部分中的项 比如搜索项 始终会出现在标签页栏的后缘
使用 sidebarOnly 放置方式 可禁止将标签页添加到标签页栏中 这样就只能通过边栏对它进行访问
要针对 SwiftUI 中的 TabView 启用用户自定功能 首先 将 TabViewCustomization 附加到 TabView 这样可为 TabView 中的标签页 启用各种自定功能
如果你有其他需要镜像 边栏自定设置的 UI 则从 TabViewCustomization 读取 以追踪自定数据
为确保自定内容能够长久保存 请在 TabViewCustomization 中 添加具有标识符的 AppStorage
然后 关联一个 customizationID 以允许标签页参与自定
现在 我已经启用了自定 我想确保 App 中的 “立即观看”或“资料库”等 重要标签页位置固定且始终可用
要停用单个标签页的自定功能 请使用 customizationBehavior 修饰符 这个修饰符让你能够控制 边栏和标签页栏中标签页的显示 “立即观看”标签页对于 App 的功能非常重要 因此 我会停用边栏和标签页栏中 标签页的自定功能
无法自定的标签页不需要 customizationID
同样 标签页也可以隐藏起来 用户可以灵活选择 App 中 显示的默认目标位置 使用 defaultVisibility 修饰符 隐藏边栏或标签页栏中的标签页
现在 我已停用“立即观看”和 “资料库”标签页的自定功能 现在 大家应该了解了 哪些是重要的 App 标签页 哪些是可自定的标签页
要在 UIKit 中启用标签页自定功能 需将 allowsHiding 设为“true” 以隐藏非必要的标签页
当前可见性由 UITab 上的 isHidden 属性决定
将标签页分配给 tabBarController 后 它保存的自定设置便会应用
使用 preferredPlacement 属性 可控制标签页栏中标签页的自定行为 及可见性
要在标签页组中重新排列标签页 需将 allowsReordering 设为“true” 自定顺序由 displayOrderIdentifiers 属性决定
自定完成后 UIKit 将调用这两个 UITabBarControllerDelegate 方法 以便在标签页的可见性和顺序 出现自定更改时通知你
自定功能让用户能够 对常用内容轻松创建快捷指令 并根据自身需求调整边栏和标签页栏 这些 API 让你可以在 Apple 平台上轻松构建标签页视图 App 我将介绍一些平台注意事项 以及你可以做出的调整 以便你 在每个平台上打造出色的用户体验 在 macOS Sequoia 中 如果 TabView 或 TabBarController 支持边栏 那么它就会采用标准的 Mac 边栏界面 跟 iPad 上的做法一样 可通过拖放 对边栏中的标签页重新排序
在 visionOS 2 中 标签页栏显示为装饰元素 位于根标签页的窗口前缘 使用新的 Tab 和 UITab API 如果符号显示在标签页栏 系统将自动选择实心图形 这跟 iOS 一样
对于 TabSection 或 UITabGroup 还会添加一个边栏 用于与标签页组的内容一起显示 以便在组内进行二级导航
在 Apple tvOS 18 中 SwiftUI App 可以使用 TabView 和 TabSection API 以采用新的可折叠边栏
借助 Tab 和 UITab 用户可以更轻松地跨 Apple 平台 构建易于导航且内容丰富的出色 App 接下来该怎么做? 借助新的标签页栏让 App 更出彩 探索新边栏如何帮助改进你的 App 并通过支持用户自定 将你的 App 个性化体验 提升至新境界 感谢大家观看 如果你喜欢使用标签页或 希望善用空间 请在下方留言
-
-
4:27 - TabView updates in SwiftUI
TabView { Tab("Watch Now", systemImage: "play") { WatchNowView() } Tab("Library", systemImage: "books.vertical") { LibraryView() } // ... }
-
4:58 - UITabBarController updates in UIKIt
tabBarController.tabs = [ UITab(title: "Watch Now", image: UIImage(systemName: "play"), identifier: "Tabs.watchNow") { _ in WatchNowViewController() }, UITab(title: "Library", image: UIImage(systemName: "books.vertical"), identifier: "Tabs.library") { _ in LibraryViewController() }, // ... ]
-
5:58 - Search tab
// SwiftUI Tab(role: .search) { SearchView() } // UIKit let searchTab = UISearchTab { SearchViewController() }
-
6:41 - Adding a sidebar in SwiftUI
TabView { Tab("Watch Now", systemImage: "play") { // ... } Tab("Library", systemImage: "books.vertical") { // ... } // ... TabSection("Collections") { Tab("Cinematic Shots", systemImage: "list.and.film") { // ... } Tab("Forest Life", systemImage: "list.and.film") { // ... } // ... } TabSection("Animations") { // ... } Tab(role: .search) { // ... } } .tabViewStyle(.sidebarAdaptable)
-
7:16 - Adding a sidebar in UIKit
let collectionsGroup = UITabGroup( title: "Collections", image: UIImage(systemName: "folder"), identifier: "Tabs.CollectionsGroup" children: self.collectionsTabs()) { _ in // ... } tabBarController.mode = .tabSidebar tabBarController.tabs = [ UITab(title: "Watch Now", ...) { _ in // ... }, UITab(title: "Library", ...) { _ in // ... }, // ... collectionsGroup, UITabGroup(title: "Animations", ...) { _ in // ... }, UISearchTab { _ in // ... }, ]
-
7:35 - Updating a tab group in UIKit
let collectionsGroup = UITabGroup( title: "Collections", image: UIImage(systemName: "folder"), identifier: "Tabs.CollectionsGroup" children: self.collectionsTabs()) { _ in // ... } let newCollection = UITab(...) collectionsGroup.children.append(newCollection)
-
7:45 - Sidebar actions
TabSection(...) { // ... } .sectionActions { Button("New Station", ...) { // action } } // UIKit let tabGroup = UITabGroup(...) tabGroup.sidebarActions = [ UIAction(title: "New Station", ...) { _ in // action }, ]
-
8:12 - Drop destinations in SwiftUI
Tab(collection.name, image: collection.image) { CollectionDetailView(collection) } .dropDestination(for: Photo.self) { photos in // Add 'photos' to the specified collection }
-
8:24 - Drop destinations in UIKit
func tabBarController( _ tabBarController: UITabBarController, tab: UITab, operationForAcceptingItemsFrom dropSession: any UIDropSession ) -> UIDropOperation { session.canLoadObjects(ofClass: Photo.self) ? .copy : .cancel } func tabBarController( _ tabBarController: UITabBarController, tab: UITab, acceptItemsFrom dropSession: any UIDropSession) { session.loadObjects(ofClass: Photo.self) { photos in // Add 'photos' to the specified collection } }
-
10:45 - TabView customization in SwiftUI
@AppStorage("MyTabViewCustomization") private var customization: TabViewCustomization TabView { Tab("Watch Now", systemImage: "play", value: .watchNow) { // ... } .customizationID("Tab.watchNow") // ... TabSection("Collections") { ForEach(MyCollectionsTab.allCases) { tab in Tab(...) { // ... } .customizationID(tab.customizationID) } } .customizationID("Tab.collections") // ... } .tabViewCustomization($customization)
-
11:40 - Customization behavior and visibility in SwiftUI
Tab("Watch Now", systemImage: "play", value: .watchNow) { // ... } .customizationBehavior(.disabled, for: .sidebar, .tabBar) Tab("Optional Tab", ...) { // ... } .customizationID("Tab.example.optional") .defaultVisibility(.hidden, for: .tabBar)
-
12:38 - Tab customization in UIKit
let myTab = UITab(...) myTab.allowsHiding = true print(myTab.isHidden) // .default, .optional, .movable, .pinned, .fixed, .sidebarOnly myTab.preferredPlacement = .fixed let myTabGroup = UITabGroup(...) myTabGroup.allowsReordering = true myTabGroup.displayOrderIdentifiers = [...]
-
12:39 - Observing customization changes in UIKit
func tabBarController(_ tabBarController: UITabBarController, visibilityDidChangeFor tabs: [UITab]) { // Read 'tab.isHidden' for the updated visibility. } func tabBarController(_ tabBarController: UITabBarController, displayOrderDidChangeFor group: UITabGroup) { // Read 'group.displayOrderIdentifiers' for the updated order. }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。