大多数浏览器和
Developer App 均支持流媒体播放。
-
桌面级 iPad 简介
学习如何在您的 iPad App 中添加桌面级功能。探索 UINavigationBar 的更新,让您 App 的功能更易于发现和便于自定义。了解 UIKit 的最新更新如何帮助用户更轻松而快速地浏览 App 中的内容。最后,我们将提供一些补充信息,与您分享如何利用 Mac Catalyst 更轻松地将 iPad App 提升到桌面级水平。
资源
- Building a desktop-class iPad app
- centerItemGroups
- searchSuggestions
- Supporting desktop-class features in your iPad app
- titleMenuProvider
- UIDocumentProperties
- UINavigationItem.ItemStyle
- UINavigationItemRenameDelegate
相关视频
WWDC23
WWDC22
-
下载
David Duncan: 大家好, 我是David Duncan 在这个视频里 我将为大家介绍 桌面级 iPad iOS 16 全新升级了 设计和构建强大 App 的工具 为前端带来更多更好用工具的Apps 充分利用了内置及附加的硬件 UIKit 增加了许多工具 可以帮助您实现 App 的这些目标 UINavigationBar 的更新 让您可以更好地利用 屏幕可显示面积 并在 Apple 所有平台 都建立绝佳的体验感
这个全新的 Find and Replace UI 是可以启用内置视图 并可轻松添加自定义视图的快照 编辑菜单已全面改造 为基于交互的 API 与菜单系统整合 Collection 视图的改进也让构建界面 前所未有的简便 您的用户可根据内容 选择及操作
您可查看“Adopt desktop class editing interactions” 获取更多关于 Find and Replace 以及编辑菜单的信息 也可查看 “Build a desktop class iPad app” 了解这些功能是如何整合协作的
在这个视频中 我将与大家 共同探讨导航的变化 这会影响您何如设计 iOS 16 的 App
首先 我们有一些新功能 能让您 更简便地构建可发现的接口 还有对基于文档的 App 来说 尤为强大的功能 最后 Search 进行了更新 从而帮助加速和改进体验感
UINavigationBar 在 iOS 中 有不同的使用目的 iOS 16 同样也搭载这一功能 提供了许多这些使用场景中 全新优化的 UI UINavigationItem 增加了一个 样式属性 可用于选择以下样式 导航 浏览和编辑 我将为大家逐一解释 默认的样式是导航 与传统 UINavigationBar 完全一样
标题居中 有前端 和后端工具栏按钮项 以及视图堆栈有超出 1 个项目时 显示的后退按钮 浏览样式重新排列了内容 从而更好地优化接口 历史记录与定位同样重要 如在 Files 或 Safari 中
在这个样式中 标题移到了前方
编辑样式的优化是针对 主要功能为 文档编辑时所做的 如浏览样式一样 标题在最前方排列对齐 编辑 UI 通常是一个终点 如用文档选择器 选择文档后 因此显示一个后退按钮 从而便于访问该 UI
浏览和编辑样式在工具栏中间 都有许多自由空间
iOS 16 正是 利用了这一自由空间 允许您将额外的控制按钮 放入该区域
中心项目是其中一项改变 以利用屏幕可显示面积 并涵盖 UIBarButtonItemGroup 支持 自定义支持及溢出
溢出支持在所有模式都可用 也让导航样式 可间接支持中心项目
单独控制继续 指定为 UIBarButtonItems 但现在如 UIBarButtonItemGroups 一样组织 这样在空间有限时可紧密展示 在这个例子中 工具栏有 5 个项目 组成了 4 个组
第一组包含一个单栏按钮项目 因此这一案例使用的是 UIBarButtonItem 的 简易方式 creatingFixedGroup 来创建的
如果您需要超过 1 个项目的固定组 可使用 UIBarButtonItemGroup 方法
固定组通常在工具栏首位 不能移动或自定义 绘制组包含一个单一项目 因此也使用了简易 API 即 creatingMovableGroup (customizationIdentifier) 如固定组一样 可移动组不能移除 但可移动
因此 需要 customizationIdentifier 来追踪和保存它们的位置 如果您需要超过 1 个项目的组 可使用 UIBarButtonItemGroup 方法
这一图形组包含多个项目 因此使用 UIBarButtonItemGroup API 来创建组
该组在工具栏中应为可移动 同时也是可移除的 因此创建为选项组
该组同样指定了 一个 representativeItem 让 UIKit 在必要时刻拆散组 从而获得更多空间
representativeItem 并不指定操作 进一步允许 UIKit 合成可选择组项目的菜单
当调用自定义 UI 时 UIKit 自动应用 您基于如何创建组 所指定的规则 当固定和可移动组 必须保留在工具栏时 可选组可用任意方式增加或移除
UIKit 会尝试粉碎组 从而保证工具栏的功能 但如果空间不可用 额外的项目将被移到工作或溢出 溢出菜单包含自定义部分的 所有不适用于工具栏的项目 以及自定义工具栏的选项
UIKit 为每个工具栏按钮项 合成默认菜单元素时 您可按需选择 自定义 menuRepresentation 最后 这一例子可自定义 以及添加 centerItemGroups
您通过设置 UINavigationItem. CustomizationIdentifier 启动自定义
customizationIdentifier 定义 工具栏的独特自定义 因此选择一个不会与 App 内 其它自定义冲突的字符串
UIKit 基于该标识符 自动保存及存储自定义
接下来 自提供 centerItemGroups 我已经提到了刚开始的四个组
格式组是可选组 不再默认自定义中 因此该代码替换 isInDefaultCustomization 参数的 默认值 从而将其排除在外 即使没有设置 UINavigationItem. customizationIdentifier 您仍可以使用 centerItemGroups 但自定义不可用 在 Mac Catalyst 中 UINavigationBar 自动将其内容 转译到 NSToolbar 前端 中间及后端组 都按顺序添加 中间组合项的 自定义属性 当使用 NSToolbar 自定义时 需谨慎处理
所有预期的 NSToolbar 行为 以及其它属性 如标题和窗口代理 均可用
这些在优化 Mac 时 都是默认触发的 接下来 我们主要来看 强大的交互功能 尤其在处理文档时 UINavigationBar 现在支持 向标题视图添加菜单 提供中央单元以添加整体操作 内容的动作 此外 您可以为共享表添加支持 及从菜单中拖拽 首先 我先着重介绍菜单项 一旦启用后 默认标题菜单 提供 5 个指令 复制 移动 重命名 导出及打印 这些项目将根据 响应者链中的特定方法进行筛选 UINavigationBar 具有对重命名的 特定支持 因此如果您执行了 renameDelegate 它也将包括在内
若要启用标题菜单 请设置 titleMenuProvider 该闭包会返回要显示的最终菜单
闭包位于建议元素数组的后面 您可以直接使用 过滤 或者添加自己的 在我们的示例中 我们将向菜单 添加一个附加操作 最后 返回合成的 UIMenu
标题菜单还允许通过 活动 view controller 进行共享 并支持拖拽
要启用这些功能 请提供描述文档的 UIDocumentProperties 实例
UIDocumentProperties 代表 文档的元数据 包括预览 这个示例创建了一个 URL 允许 UIKit 自动获取必要的元数据
为启用其它功能 此示例创建一个 NSItemProvider 来代表文档
设置 dragItemsProvider 以启用拖拽功能 此闭包经过 UIDragSession 并返回 UIDragItems 数组 此示例返回代表文档的单个项
设置 activityViewControllerProvider 可启用共享 该闭包配置并 返回一个 UIActivityViewController
最后 将填写好的对象分配给 UINavigationItem.documentPropertiesc 当点击标题时 UIKit 会将 header 与其他 titleMenu 项一起显示
在 Mac Catalyst 上 即将传递给 titleMenuProvider 的 建议项目 已经存在于 File 菜单中 您要添加到标题菜单的任何项目 都需要通过其它方式提供
您可以使用 UIMenuBuilder API 添加这些项目 或根据需要过滤现有项目
如果您指定了文档属性 UIKit 将自动使用 提供的 URL 来管理 macOS 代理图标
如果手动设置 windowScene 的 representedURL 则将取代 UIKit 的管理
UIKit 提供了两种机制来启用重命名 内联重命名是通过设置 UINavigationItem.renameDelegate 来提供的 它为在所有平台上编辑标题 提供了一个专门的 UI
完成后 生成的名称将传递给代理
或者 您可以通过 执行 UIResponder.rename (_:) 并提供您喜欢的任何 UI 来完全控制重命名体验
在iOS上 UINavigationBar 直接在 标题视图中提供重命名 UI 在 macOS 上 当导航栏位于 NSToolbar 中时 窗口的标题将提供重命名 UI 要实现内联重命名 请遵循 UINavigationItemRenameDelegate 协议 并设置导航项的 renameDelegate 只有一个必需的方法 即 NavigationItem(_:didEndRenamingWith:) 用于接收用户接受的标题
对于基于文件的 app 即 UIDocumentBrowserViewController 现在提供了一个重命名的 API
Search 是指有多少用户 找到他们最重要的数据 iOS 16 的进步使其更容易 提供出色的搜索体验 首先要注意的是 iPadOS 上的导航栏 和 MacOS 上的工具栏 现在排成一排 搜索占用的空间更少了 在 iPadOS上 您可以用 UINavigationItem.preferredSearchBarPlacement 恢复历史行为 此外 搜索栏可以粉碎成一个按钮 为其它控件提供更多空间 当搜索被激活时 将显示搜索建议 并且可以将其与 更新搜索查询一起更新 从而使您有机会帮助用户进行搜索 接下来 我将介绍设置搜索建议 所需的代码
要管理搜索建议 请遵循 UISearchResultsUpdate 并设置 searchController 的 SearchResultsUpdate 这使您可以在查询更改时更新建议 并对选定的搜索建议执行操作
当查询更改时 将调用 updateSearchResults(for:) 允许您更新搜索建议
您可自行决定提供什么建议 设置空数组将清除建议 UI
UIKit 提供 UISearchSuggestionItem 来指定建议内容
要响应建议的选择 请执行 updateSearchResults(for:Choosing:) 该方法传递所选择的搜索建议 因此您可以对它作出适当的响应 在本例中 我通过将当前查询 替换为搜索建议指定的查询 来更新搜索 UISearchTextField 也有 searchSuggestions 所以如果您喜欢单独使用该类 仍然可以执行搜索建议 但如您使用的是 UISearchController 则应改用其属性
在 iOS 16 中 UIKit 提供了 新的 API 来帮助您提高用户的工作效率 通过中间项目和标题菜单 为高级功能带来更多可查找性
通过直接从导航栏 提供拖拽和共享功能 改进文档支持 通过提供搜索建议 让搜索变得更容易 更快 并立即获得良好的 Mac 体验 且几乎不费吹灰之力 感谢您的观看 我迫不及待想看您是如何 将 app 提升到桌面级了
-
-
4:27 - Creating a fixed UIBarButtonItemGroup from a single UIBarButtonItem
let insertGroup = UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup()
-
4:55 - Convenient form
// Creating the 'Draw' group // Convenient form of // UIBarButtonItemGroup.movableGroup(customizationIdentifier:representativeItem:items:) let drawGroup = UIBarButtonItem(title: "Draw", …) .creatingMovableGroup(customizationIdentifier: "Draw")
-
5:30 - Creating an optional group with multiple UIBarButtonItems using UIBarButtonItemGroup
let shapeGroup = UIBarButtonItemGroup.optionalGroup( customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ])
-
6:56 - Setting up customizable centerItemGroups on a UINavigationItem
navigationItem.customizationIdentifier = "com.jetpack.blueprints.maineditor" navigationItem.centerItemGroups = [ // groups in the default customization UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup(), UIBarButtonItem(title: "Draw", image: UIImage(systemName: "scribble"), primaryAction: UIAction { _ in }).creatingMovableGroup(customizationIdentifier: "Draw"), .optionalGroup(customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ]), .optionalGroup(customizationIdentifier: "Text", items: [ UIBarButtonItem(title: "Label", image: UIImage(systemName: "character.textbox"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Text", image: UIImage(systemName: "text.bubble"), primaryAction: UIAction { _ in }), ]), // additional group not in the default customization .optionalGroup(customizationIdentifier: "Format", isInDefaultCustomization: false, representativeItem: UIBarButtonItem(title: "BIU", image: UIImage(systemName: "bold.italic.underline")), items:[ UIBarButtonItem(title: "Bold", image: UIImage(systemName: "bold"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Italic", image: UIImage(systemName: "italic"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Underline", image: UIImage(systemName: "underline"), primaryAction: UIAction { _ in }), ]) ]
-
9:30 - Adding a "Comments" item to the default title menu
navigationItem.titleMenuProvider = { suggestedActions in var children = suggestedActions children += [ UIAction(title: "Comments", image: UIImage(systemName: "text.bubble")) { _ in } ] return UIMenu(children: children) }
-
10:15 - Supporting Drag & Drop and Sharing from the title menu
let url = <#T##URL#> let documentProperties = UIDocumentProperties(url: url) if let itemProvider = NSItemProvider(contentsOf: url) { documentProperties.dragItemsProvider = { _ in [UIDragItem(itemProvider: itemProvider)] } documentProperties.activityViewControllerProvider = { UIActivityViewController(activityItems: [itemProvider], applicationActivities: nil) } } navigationItem.documentProperties = documentProperties
-
12:45 - Implementing inline rename
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.renameDelegate = self } } extension ViewController: UINavigationItemRenameDelegate { func navigationItem(_ navigationItem: UINavigationItem, didEndRenamingWith title: String) { // Try renaming our document, the completion handler will have the updated URL or return an error. documentBrowserViewController.renameDocument(at: <#T##URL#>, proposedName: title, completionHandler: <#T##(URL?, Error?) -> Void#>) } }
-
14:05 - Implementing Search Suggestions
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() searchController.searchResultsUpdater = self } } extension ViewController: UISearchResultsUpdating { func fetchQuerySuggestions(for searchController: UISearchController) -> [(String, UIImage?)] { let queryText = searchController.searchBar.text // Here you would decide how to transform the queryText into search results. This example just returns something fixed. return [("Sample Suggestion", UIImage(systemName: "rectangle.and.text.magnifyingglass"))] } func updateSearch(_ searchController: UISearchController, query: String) { // This method is used to update the search UI from our query text change // You should also update internal state related to when the query changes, as you might for when the user changes the query by typing. searchController.searchBar.text = query } func updateSearchResults(for searchController: UISearchController) { let querySuggestions = self.fetchQuerySuggestions(for: searchController) searchController.searchSuggestions = querySuggestions.map { name, icon in UISearchSuggestionItem(localizedSuggestion: name, localizedDescription: nil, iconImage: icon) } } func updateSearchResults(for searchController: UISearchController, selecting searchSuggestion: UISearchSuggestion) { if let suggestion = searchSuggestion.localizedSuggestion { updateSearch(searchController, query: suggestion) } } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。