大多数浏览器和
Developer App 均支持流媒体播放。
-
采用桌面级编辑交互
了解高级桌面级编辑功能,它们可以帮助您 App 的用户提高效率。学习如何利用 Mac Catalyst 来提供更多内嵌在 UI 中的交互,帮助用户快速访问编辑功能并在 macOS 上得心应手地使用您的 iPadOS App。我们还将探索高度可自定的查找互动功能,了解系统 UI 如何帮助用户流畅一致地查找您 App 中的内容。
资源
- Building a desktop-class iPad app
- Supporting desktop-class features in your iPad app
- UIEditMenuInteraction
- UIFindInteraction
相关视频
WWDC22
WWDC19
-
下载
Andy: 大家好 欢迎来到“Adopt desktop class editing interactions” 我是 Andy 一名 UIKit 框架工程师 稍后我的同事 James 也会加入 iPad 一直在不断优化 但不会影响使其简单易用的交互 在本视频中 大家将了解令人兴奋的 全新编辑交互工具 使你们的应用程序变得桌面类 首先 我将介绍一下新的编辑菜单 它在 iOS 16 中有了重大改进
稍后 James 将深入探讨新系统的 查找和替换经验 在iOS 16 中 编辑菜单 采用了一种全新的设计 这种设计既熟悉又更具交互性 并且更容易发现操作 编辑菜单现在具有 基于所有输入法的替代显示 对于触摸交互 编辑菜单仍然具有熟悉的紧凑外观 但改进了调页行为 使操作比以前更容易被发现
通过妙控键盘或触控板 在二次点击或右击时 会显示上下文菜单 以获得更多桌面类体验 同样 iPhone 上的触摸交互 也会显示新的编辑菜单
对于 Mac Catalyst 应用程序 它会提供 Mac 用户 熟悉的上下文菜单 在 iOS 16 中 文本编辑菜单 集成新的数据探测器 获得了重大提升 这包括内联单元和货币转换 以及根据 所选文本显示上下文操作的 智能查找 例如 如果你在 Safari 浏览器中 选择一个地址 那么你就会在 现有的编辑菜单操作之上 获得基于地图的操作 比如 Get Directions 或 Open in Maps 最棒的是 不需要采用 这些功能在每个 文本编辑菜单中都可用 包括文本交互视图 WebKit 和 Safari 以及 PDFKit
要在文本视图的菜单中插入操作 执行新的 TextViewDelegate 方法 用系统提供的操作 为给定范围内的文本 自定义显示菜单 如果你不需要自定义任何内容 请返回 nil 以获取标准系统菜单 UITextFieldDelegate 和 UITextInput 也有类似的方法来自定义菜单 请注意 现在在 iOS 16 中已不支持 使用 UIMenuController 来插入菜单项 你们应该使用新方法将菜单元素 添加到你的文本编辑菜单中 因为我们的目标是 不再需要 menu controller 这是一个带有一些自定义操作的 文本视图示例 当在某些文本选择上显示菜单时 系统建议的操作后面会显示自定义的 高亮和插入图片 选择高亮显示操作 将按预期对文本执行高亮显示 接下来 当菜单没有任何文本选择 而没有突出显示任何内容时 菜单只会在系统建议的操作之后 显示插入照片操作 我将向你们展示如何 使用新的 API 添加这些操作 要将操作插入到动态菜单中 执行 UITextViewDelegate 方法 textView editMenuForTextInRange: suggestedActions 在这个例子中 我只想在有选中文本时 添加高亮显示动作 所以我可以 通过这个方法动态添加动作
Insert Photo 操作始终有效 所以我可以将它添加到数组中 总是在菜单中显示 最后 我将把我的操作 添加到系统建议的操作中 其中包括剪切 复制和粘贴等项 然后返回菜单 就是这样 UIEditMenuInteraction 是支持 新编辑菜单的 UIInteraction API
此交互允许你们以编程方式 基于自己的手势 在文本视图之外 显示轻量级编辑菜单 并支持在二次单击时 显示上下文菜单 在 iOS 16 中 UIMenuController 及其所有相关 API 都被新的编辑菜单交互所取代
要从头开始呈现编辑菜单 首先 创建交互 并将其添加到视图中 接下来 配置一个手势识别器 来显示菜单 为确保菜单仅在直接触控 而不是间接光标单击时出现 请确保将手势识别器的 allowedTouchTypes 属性 设置为仅支持直接触控 然后 将手势识别器添加到视图中 最后 当手势识别器触发时 确定手势所在的位置 是否有可以显示菜单的内容 然后 在手势位置创建一个 带有源点的编辑菜单配置 源点用于确定交互视图中 要显示在菜单中的可执行操作
配置完成后 调用 presentEditMenuWithConfiguration 来显示菜单
当我右键单击所选的 Jello there 视图中的任意位置时 上下文菜单显示了 App 内容的 可执行系统操作 甚至 当我点击选中的视图时 编辑菜单就会出现在我的触控位置 显示与上下文菜单相同的操作 这很好 但还能更好 虽然菜单出现在触控的地方很好 但它实际上阻止了所选视图的内容 此外 我想在菜单中插入 一个新的 Duplicate 操作 这不是系统默认操作 我们返回修改一下 要在选定的视图周围显示菜单 实施委托方法 editMenuInteraction: targetRectForConfiguration: 这个方法返回一个 CGRect 用于确定从哪里显示菜单 并且位于交互视图的坐标空间中 如果没有实施该方法 或提供了一个空的 CGRect 则菜单将从配置的源点显示 在这种情况下 为防止菜单遮挡所选视图 返回它的参照系 接下来 要添加 Duplicate 操作 执行 editMenuInteraction: menuForConfiguration:suggestedActions: 并在系统建议的操作之后 添加自定义操作 类似于之前将操作插入 文本视图菜单的方式
现在 当我再次点击所选视图时 菜单不再遮挡 Jello there 而是围绕它显示 出现菜单时还包括新的 Duplicate 操作 只需要几行代码 棒极了
对于 Mac Catalyst 应用程序 编辑菜单连接到熟悉的上下文菜单 用户在 Mac 上 右键单击交互视图时就会看到 对于 iPad 惯用的 Mac Catalyst 应用程序 以编程方式呈现的编辑菜单 也可连接到上下文菜单 请注意 Mac idiom 应用程序 不支持编辑菜单的 程序化显示 为了在不同的演示风格之间 提供无缝衔接 UIEditMenuInteraction 构建在 UIMenuElement 系列 API 之上 它们提供了比之前更大的灵活性 和可定制性 包括对子菜单和图像的支持 如果这是你们第一次使用 UIMenus 请观看“Modernizing Your UI for iOS 13” 以了解有关菜单和操作的更多信息 建立在 UIMenuElement 之上 还意味着 编辑菜单可访问已经支持它的 各种 API 比如 UIMenuSystem 编辑菜单使用现有的 UIMenuSystem.context 系统 来构建其菜单 要了解更多关于菜单生成器的信息 以及更深入地了解 响应链遍历和命令验证 请观看“Taking your iPad apps to the next level”
说到菜单 iOS 16 中的 UIMenu 做了几项新的改进 UIMenu 现在有一个首选元素大小属性 可以在上下文菜单的 不同布局之间进行选择 较小的尺寸使菜单具有 更紧凑的并排外观 在一行中可容纳更多操作 中等大小也以并排的外观显示操作 但有更多的细节 文本编辑菜单用它来 显示标准编辑菜单 最后 大单元尺寸为菜单提供了默认的 全屏宽度外观 此外 UIMenuElement 上 有一个新的 keepMenuPresented 属性 以在执行操作后 保持显示菜单 使用此属性可允许多次执行操作 而无需重新显示菜单 这些只是新编辑菜单的冰山一角 通过自定义文本编辑菜单 扩展文本编辑功能 确保你们的操作具有标题和图像 以便菜单在不同的显示样式中 看起来是完整的 最重要的是 采用新的 UIEditMenuInteraction 来获得更好的可定制性 并提高跨平台 以及不同输入法的一致性 第一步添加 对新编辑菜单的支持是不错的 为了完成桌面类编辑体验 我将让 James 来谈谈 新系统的查找和替换体验
James Magahern: 啊 它在那儿 大家好 我是 James Magahern 一名 UIKit 工程师 我今天要讲的是查找和替换 在 iOS 16 中 我们推出了一个新的 UI 组件 以查找和替换应用程序中的文本 它是整个系统的标准配置 包含在许多内置应用程序中 允许用户使用更常用的编辑快捷方式 来锻炼他们的肌肉记忆 这是在 iPad 上运行的新发现面板 当连接了硬件键盘时 我们将自动从 与快捷栏的浮动内联 过渡到没有硬件键盘时 静止于软件键盘顶部 在 iPhone 上 我们将使用更紧凑的布局 来适应更小的屏幕尺寸 自动退出 最小化和 keyboard avoidance 均由系统负责 在 Mac 上运行 App 时 我们将显示与内容内联的查找面板 就像 AppKit 的查找栏一样 并使用用户期望在 Mac 上的熟悉布局
如果你们使用 UITextView WKWebView 或 PDFViews 在应用程序中显示文本内容 你们所需要做的就是在 内置查找交互中 将 isFindInteractionEnabled 设置为真 很简单吧 另外 如果你们想要使用 QuickLook 来显示文本内容 这将无需你的任何操作就可用
使用硬件键盘 所有标准系统快捷方式 例如 command+F 用于查找 command+G 用于查找下一个 command+shift+G 用于查找上一个等 都可以正常运行 当在 Mac 上运行时 可以通过菜单栏访问这些命令 你所需要做的就是确保 显示内容的视图 能够并且确实成为第一响应者 对于不使用硬件键盘的用户 可以通过 presentFindNavigator 在包含的查找交互属性上 以编程方式调用查找交互 例如 通过导航条项目使用 可能是个不错的方法
在 Mac 上运行时 还有几件事需要记住 例如 在 iOS 上 查找面板 显示为软件键盘或快捷栏的一部分 在 Mac 上 我们会将其显示在你的内容中 如果你们在滚动视图上安装查找交互 我们将自动调整内容插入 以适应查找面板 并自动适应特征集合的更改 否则 你应该确保在 macOS 上的 UI 中 有足够的空间来存储查找面板
此外 点击放大镜图标时 会显示一个包含一组 标准查找选项的菜单 你可以使用 UIFindInteraction 上的 optionsMenuProvider 属性 来定制该菜单的内容 这对于实现自定义更为重要 如果你在使用的是我之前提到的 一个内建视图 那么就是这些了 如果你的 App 通过其他方式 显示文本内容 比如完全自定义视图或列表视图 如这里所示 那么你仍然可以 将查找交互添加到应用中 我来展示一下怎么添加
关于查找交互的好消息是 你可以将其安装在任意视图上 如果你的 App 中 有现成的查找和替换实现 则可以快速过渡到 UIFindInteraction 并利用系统的 UI 如果你们还没有现成的 用于自定义视图的查找实现 那么上手仍然非常容易 特别是如果你们已经实现了 UITextInput 协议 以便与系统键盘一起工作 以下是 UIFindInteraction 如何与自定义视图一起使用 在自定义视图上安装 UIFindInteraction 后 设置查找交互委托 查找交互委托 除了接收通知 查找讲座何时开始或结束 还负责处理 UIFindSession UIFindSession 是一个抽象基类 它压缩了给定讲座的所有状态 如当前高亮显示的结果 它还为从 UI 请求的所有操作提供服务 例如“go to the next result” 或 “search for this string” 如果你想自己管理所有这些状态 那么你可以从查找交互委托中选择 提供 UIFindSession 的子类
如果你的 App 中已经有了一个 现成的查找和替换实现 并且希望将其连接到系统 UI 这是个不错的选择 另外 让系统为你处理状态 将是个更好的主意 而不是在 任何封装显示文档内容的类上 采用 UITextSearching 协议 为此 你将返回一个 UITextSearchingFindSession 并将它与你的文档类连接 如果你还没有 自定义视图的查找实现 这是最好的选择 以下是如何在代码中执行此操作
这个例子有一个自定义文档类 和一个显示该文档的自定义视图 UIFindInteraction 将安装在这个视图上 UITextSearchingFindSession 将作为 searchable object 随该文档一起提供 确保你的视图控制器或自定义视图 可以成为第一响应者 这样键盘快捷方式就能正常工作
创建查找交互 并提供讲座委托 来处理查找讲座 这里 视图控制器就是讲座委托 然后 当交互询问查找讲座时 只需返回一个新的 UITextSearchingFindSession 将你的文档提供为可搜索对象 当然 你需要确保自己的文档类 符合 UITextSearching 协议
实现 UITextSearching 协议的类 负责在文档中实际查找文本 系统将调用 performTextSearch 并为你提供一个聚合器对象 你可以向其提供结果 聚合器与 UITextRange 一起使用 以表示文档中的结果 这是另一个抽象类 你可以用它 来封装任何对你如何 存储文本有意义的数据 例如 这可以代表 使用 WebKit 呈现文本的 客户端的 DOM 范围 聚合器也是线程安全的 因此你们可以在后台线程上 为它提供结果 最后 由于发现交互 不知道如何使用自定义视图显示结果 因此你还需要在调用 decorate 时 为给定样式装饰结果 UITextSearching 查找讲座和协议 还支持使用相同的交互 跨多个可见文档 进行多路复用 换句话说 如果你的 App 以类似于 Mail 的对话视图的方式 显示内容 在这种情况下 每个 document 都是一个邮件消息 你可以在根级集合视图上 安装单个查找交互 同时在所有文档中执行查找 从而使你的用户可以轻松地 在不同文档结果之间跳转 这就是开始使用 iOS 16 中 新的查找交互所需的全部内容 对于显示大量文本内容的系统视图 请确保启用 isFindInteractionEnabled 将现有的查找实现移动到 UIFindInteraction 如果你的 App 中还没有文本搜索 请执行 UITextSearching 并使用 UITextSearchingFindSession 最后 检查并确保你的 App 中 没有任何冲突的键盘快捷键 这就是为 iOS 16 刷新你的 App 的编辑交互 并使它们成为 真正的桌面类所需要做的 在你的 App 中尝试新的文本编辑菜单 并采用自定义 UI 的编辑菜单交互 通过使你的 App 的文本内容可搜索 来提高工作效率 我期待着在你的 App 中 发现这些很棒的新功能 感谢收看 一定要点赞 评论并订阅哦
-
-
2:42 - Adding items into text edit menus
func textView( _ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu?
-
4:03 - Adding actions into a text view's menu
func textView( _ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement] ) -> UIMenu? { var additionalActions: [UIMenuElement] = [] if range.length > 0 { let highlightAction = UIAction(title: "Highlight", ...) additionalActions.append(highlightAction) } let insertPhotoAction = UIAction(title: "Insert Photo", ...) additionalActions.append(insertPhotoAction) return UIMenu(children: suggestedActions + additionalActions) }
-
5:24 - Presenting an edit menu with a custom gesture
let editMenuInteraction = UIEditMenuInteraction(delegate: self) view.addInteraction(editMenuInteraction) let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) tapRecognizer.allowedTouchTypes = [UITouch.TouchType.direct.rawValue as NSNumber] view.addGestureRecognizer(tapRecognizer) @objc func didTap(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: self.view) if self.hasSelectedObjectView(at: location) { let configuration = UIEditMenuConfiguration(identifier: nil, sourcePoint: location) editMenuInteraction.presentEditMenu(with: configuration) } }
-
7:13 - Implementing UIEditMenuInteractionDelegate
func editMenuInteraction( _ interaction: UIEditMenuInteraction, targetRectFor configuration: UIEditMenuConfiguration ) -> CGRect { guard let selectedView = objectView(at: configuration.sourcePoint) else { return .null } return selectedView.frame } func editMenuInteraction( _ interaction: UIEditMenuInteraction, menuFor configuration: UIEditMenuConfiguration, suggestedActions: [UIMenuElement] ) -> UIMenu? { let duplicateAction = UIAction(title: "Duplicate") { ... } return UIMenu(children: suggestedActions + [duplicateAction]) }
-
10:34 - Using the "keeps menu presented" attribute
UIAction(title: "Increase", image: UIImage(systemName: "increase.indent"), attributes: .keepsMenuPresented) { ... } UIAction(title: "Decrease", image: UIImage(systemName: "decrease.indent"), attributes: .keepsMenuPresented) { ... }
-
12:46 - Find with system views
open var findInteraction: UIFindInteraction? { get } textView.isFindInteractionEnabled = true
-
17:22 - Installing a UIFindInteraction on a custom view
let customDocument = MyDocument(string: "") lazy var customView = MyTextView(document: customDocument) lazy var findInteraction = UIFindInteraction(sessionDelegate: self) override var canBecomeFirstResponder: Bool { true } override func viewDidLoad() { customView.addInteraction(findInteraction) } func findInteraction(_ interaction: UIFindInteraction, sessionFor view: UIView) -> UIFindSession? { return UITextSearchingFindSession(searchableObject: customDocument) }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。