大多数浏览器和
Developer App 均支持流媒体播放。
-
Mac Catalyst 的辅助功能设计
使你的 Mac Catalyst app 对所有人都可用,并将这些改进带回您的 iPad app。了解在添加对 Mac Catalyst 的支持后,功能强大的 iPad app 如何自动变为功能强大的 Mac app。了解如何通过支持鼠标和键盘操作以及辅助功能元素分组和导航来进一步增强您的体验。探索如何使用新的辅助功能检查来测试你的 app 并进行迭代,为每个人创造真正的绝佳体验。 为了充分利用本次分享,你应该熟悉 Mac Catalyst,UIKit 和 iOS 的基本可访问的 API。要开始使用,请查看“为 Mac 引入 iPad app” 和“审核你的 app 可访问性”。
资源
相关视频
WWDC19
-
下载
(你好 WWDC 2020)
你好 欢迎来到 WWDC (Mac Catalyst 的辅助功能设计) 大家好 我是 Eric 待会儿我的同事 Nathan 也会加入 我们会一起分享 Mac Catalyst 的辅助功能设计 Mac Catalyst 是 Apple 的一项巨大成就 它超级简单易用 而开发者社群也非常喜爱它 在 App Store 上 我们已经上架了一些使用 Mac Catalyst 而创建出来的好用 app 通过这么多的得意作品 你就知道 将你的 app 打造成 适合所有用户使用有多么重要 这就是我们今天分享 关于辅助功能课题的原因 在 Apple 这里 辅助功能是我们的一个核心价值 我们的所有平台都具有大量的辅助技术 以帮助身心障碍人士 我们的辅助功能团队非常努力地 确保你为 iOS 辅助功能 所作出的一切心血 都能被转换成 Mac Catalyst 因此 如果你的 iOS app 具有辅助功能 当你将它引进 Mac 时 它也会自动具有辅助功能 这是为了让你 在创建 Mac Catalyst app 时 能够继续以 iOS 的形式进行开发 我们会先讨论通过改进焦点行为 且添加键盘快捷指令 来优化键盘使用的 app 体验 接着 我们会带你了解 如何为辅助技术而增强 app 的导航效率 最后 我们还会给出在 macOS 上 测试 Mac Catalyst app 辅助功能的建议 先从键盘使用开始吧 macOS 上的键盘 和它在 iPadOS 上的定位不一样 它不仅仅是一个附加的互动方式 反而 它是用户和你 app 互动的首要媒介 因此你的目标是 尽可能地把 app 功能设计成 可以让键盘访问 为此 你可以做的第一件事 是检查键盘焦点 键盘焦点会确定当前的哪个用户界面元素 正在接收来自键盘的输入 而我们也要确保 可以与用户互动的 所有 app 用户界面元素都能够成为焦点 要示范这点 我们先来看看这个 叫做 Roasted Beans 的示例 app 它是 Peanut Butter app 的衍生应用 后者是我们几年前推出的辅助功能 app 有鉴于 Peanut Butter app 的巨大成功 很多人一直询问我们 想要寻找能够配合 他们的花生厚吐司一起享用的绝佳咖啡 所以 完成构建这个 iOS app 并让它具有辅助功能后 现在我们将会使用 Mac Catalyst 把它带到 macOS 上
来看看这个 app 如何和键盘互动吧 要测试这点 我们需要先开启系统设定 启用可以与控件互动的键盘功能 这个功能就位于“系统偏好” app 的
“键盘”部分里 就在“快捷指令”菜单下方 通过复选框 你可以 使用键盘导航在控件之间移动焦点
然后回到我们的 app 看看轻按标签页后会怎样吧 现在我们会看到 导航栏右边的“添加”按钮周围 有一个焦点环 这就代表“添加”按钮现在具备了键盘焦点 如果你轻按空格键 该按钮就会被激活 下一个标签页加亮了侧边栏的第一件物品 用户可以轻按上下箭头键 来更改表视图的选择
接下来的标签页加亮了 app 里的其他可互动控件 例如“共享”按钮、“收藏”按钮 和“赠品”按钮
哇 这真是太神奇了 我们甚至连动动手指头都不用 UIKit 就已经使我们的所有控件 变得可通过键盘访问 标签页键让我们可以 在内部使用 app 里的控件 箭头键则让我们可以更改表视图的选择
如果你 app 里的任何地方 有 UITableView 或者 UICollectionView 你可能会发现箭头键并不会移动选择 要想让箭头键移动选择的话 你只需要在 UITableView 或 CollectionView 上将这个新 API 即 selectionFollowsFocus 设置成 true 即可 在示例 app 中 由于 tableView 是 UISplitView 的侧边栏 UIKit 就帮我们将这个设置成 true 因此我们可以跳过这个步骤
欲进一步了解 更全面自定义键盘体验的指南 你可以查看这个示例 app 并观看 “What's New in Mac Catalyst” 的讲座 现在我们已经确定 app 的所有可互动控件都可以成为焦点 我们可以接着讨论 你能为键盘使用所做的下一步 即添加键盘快捷指令 此时此刻 如果我们的用户 想要添加新咖啡或评价咖啡 他们只能通过点击屏幕来做到这点 对于使用辅助技术的人们来说 寻找屏幕上的用户界面 以进行互动可能是很累人的事
假设用户能通过便捷的键盘快捷指令 来执行操作的话 这就再好不过了 让我们一起来看看 为了和朋友共享新鲜烘焙的咖啡豆 我们要为添加键盘快捷指令做什么吧 要做到这点 首先我需要 先找出最好的键盘搭配 因为我们要让客户 尽可能地觉得直观且方便 你可以先去查看 Apple 指南 以获取常用的键盘快捷指令列表 如果你的 app 有相似的功能 你可以考虑将这些键盘搭配 应用在那些快捷指令上 因为用户已经相当熟悉这样的操作了 (键盘组合 遵循指南) 对我们而言 “共享”并不在列表上 所以另一个办法便是 查看 Mac 上的现有 app 看看它们有没有相似的快捷指令 所以我查看了 Mac 上 人人都喜爱的 Safari 浏览器 app 它的“共享”快捷指令是 Command-I 而我决定要效仿它
要做到这点 首先我们要 在 AppDelegate 里超控 buildMenu 函数
我们需要创建一个响应键盘快捷指令的 UIKeyCommand 我们将本地化的字符串指派成标题 因为它会显示在菜单栏中 然后我们再指派一个 会被该指令所触发的操作 我们需要创建一个响应键盘快捷指令的 UIKeyCommand 我们将本地化的字符串指派成标题 因为它会显示在菜单栏中 然后我们再指派一个 会被该指令所触发的操作 并将 modifierFlags 指令的输入 设置成字母 “I”
然后我们需要创建一个 UIMenu 它会将 shareCommand 视作菜单的唯一子项目 然后我们再将这个新的菜单项目 插入到菜单栏的一个合适位置上 在这次的演示当中 我选择 将它插入到“编辑”菜单的最后部分 如果你打开“编辑”菜单 你会看到我们的新键盘快捷指令 即“共享” 已经美美地出现在了“编辑”菜单下面 而你可以通过轻按 Command-I 访问它
还有别忘了 你为 Mac Catalyst 所作出的一切优化努力 也将成为 iOS 上 完整键盘访问的绝佳优化 这是一个特性 它允许用户使用只具备键盘的设备
除此之外 从 iOS 13.4 起 你的 app 还可以 从 UIPress 对象上 获取一模一样的 keyCode 比方说 你在开发一个游戏 这就会特别有用 因为这个 API 能让你完全控制键盘 记得要查看我们的示例 app 以了解这些使用 来个快速回顾吧 为键盘而设的优秀 app 即是为辅助功能而设的优良 app
确保可互动控件能通过键盘焦点来访问 是非常重要的
我们建议你为你的 app 添加一些实用的键盘快捷指令
这就说完了键盘使用的部分 现在我会把时间交给我的同事 Nathan 他会和你们说明如何改进辅助技术的 导航效率
谢谢 Eric 朋友们 我是 Nathan 我是辅助功能团队的一名软件工程师 我和我的团队一直都很努力地 改进我们的 macOS 辅助技术 以便更好地和你的 Catalyst app 互动 如 Eric 之前所说的那样 让你的 app 具有辅助功能 等于将它变得人人都可用 要将你的 app 变得人人都可用 其中一步就是 要给予用户一个访问内容的有效方法 今天我们会专注于旁白 这是存在于我们所有平台上的 一个屏幕阅读器 它通过读出屏幕上的内容 使低视力或失明用户 能够与你的 app 互动 旁白通过与基于你用户界面的 辅助功能元素树 进行互动来做到这点
你可能会在 macOS 上推出更复杂的 app 它具有更大的用户界面 因为你想充分利用额外的屏幕空间 更复杂的界面 意味着旁白需要导航更多的辅助功能元素 这也代表着用户需要有效的导航方式 不然他们可能会开始晕头转向 这也是我今天非常兴奋 想和你们讨论辅助功能分组的原因 你可以使用这个策略 来为旁白用户改进 app 的导航 我们现在已经能够使用分组 来提供更原生的 macOS 体验 但是在我们讨论改进导航效率之前 我们需要先明白旁白如何理解你的 app 这是旁白理解 app 的方式 我们将其称为辅助功能树 这是我们所有辅助技术 都可以看得见的元素集合
辅助功能元素的视图 是由 isAccessibilityElement 属性 所决定的 每一个元素都是一个叶节点 它们最终会汇集成单一层级的元素树 这个模型在 iOS 上好用极了 用户通过触摸屏来进行导航 这意味着他们可以一个接一个地导航元素 或者也可以通过轻点屏幕 快速跳转到该元素 可是在 macOS 上 旁白用户需要使用键盘来导航 在没有触摸屏的情况下 他们无法在元素之间快速跳转 因此 如果我们使用了相同的模型 他们就需要一个接一个地 导航至所有辅助功能元素 为阐明这项导航挑战 我们先来看看 Roasted Beans app 的 辅助功能元素 你可以看到这里有着 26 个可见的辅助功能元素 这意味着 任何时候 用户都至少需要在 26 个元素间导航 试想想如果你需要在 Xcode 里通过 26 次键击才能去到“编译”按钮 你可能就会觉得编程好像有点太难了 虽然你可以添加键盘快捷指令 对特定元素跳过这样的导航步骤 但是这并非所有用例的解决之法 所以我们有什么改进这项体验的方法吗? 如果说我们从晚餐菜单中汲取灵感呢? 我们不要一份列举每一样菜品的长菜单 反而 我们将它们分类进相关部分 比如说沙拉、主菜和配菜 这会允许旁白用户 在导航至特定分组内的单一元素前 先在分组之间导航 我们也可以将这个主意应用到辅助功能树 你可以通过辅助功能容器的使用 来定义元素之间的关联 当你在一个元素上设置 accessibilityContainerType 我们的辅助功能技术就能够使用该信息 来为其包含的辅助功能元素 提供更好的导航体验
你可能已经了解辅助功能容器 API 以及它能如何帮助 iOS 用户的导航 打个比方 这个 API 能允许他们执行触碰手势 以便导航至下一个容器的 第一个辅助功能元素 旁白会因此专注于这样的元素
由于我们的目标是 改进 Mac Catalyst 的导航效率 因此 macOS 的旁白也会运用这样的 API 不过我们会修改行为 macOS 的辅助功能容器 会像辅助功能元素一样运作 这代表着旁白可以将焦点导航至容器本身 在这里 用户可以 选择跳过容器去到下一个元素 或者与容器互动 这允许我们进行 容器辅助功能元素外的导航操作 我说过辅助功能元素容器 会像辅助功能元素一样运作 因为当我们为 Mac Catalyst app 构建辅助功能树时 该容器会自己成为树上的节点 而它的辅助功能元素也会成为自己的子树 这种结构和围绕 AppKit 所建立的辅助功能 API 高度一致
它的模型在 macOS 上非常好用 因为它大大地减少了用户在任意时候 需要进行导航的辅助功能元素数量
这里的关键要点是辅助功能容器 在 Mac Catalyst 上是辅助功能元素 因此你要确保自己已经在容器上 设置好了辅助功能标签 这至关重要 回到我们的 Roasted Beans app 现在 辅助功能容器会在辅助功能树里 像独立节点一样运作 我们又会看到什么呢?
在这里 你可以看到惊人的效率改进 它把可见元素的数量从 26 减到了 8 现在我们已经了解辅助功能容器 对导航的显著功用 现在我们花点时间讨论一下不同的类型 以及合适的使用时间吧 数据表是专门为采用了 UIAccessibility- ContainerDataTable 协议的容器而设的 比如说图表 列表则是为有序内容而设的 这些主要用于网页或者 PDF 的目录表里 地标是专门用在网页和 tvOS 上的容器 最后 semanticGroups 是 iOS 上的一般容器类型 在 iOS 上 当旁白用户 在该容器里首次专注于某个元素时 它们的辅助功能标签就会被念出来 在 macOS 上 当旁白专注于容器本身时 它们的标签就会被念出来
至于我们的情况 我们想要使用 semanticGroup 类型 来改进导航体验 虽然分组会大大地改进导航体验 可是我要确保你充分了解到 过多的分组会使 你的 app 导航变得太过复杂 由于每个辅助功能容器 都成为了辅助功能树上的一个节点 除非旁白用户能够明确地与该组互动 否则容器内的元素是不可发现的 来看看我们应该在什么时候分组元素吧 首先 隶属于相同功能区的元素 应该被分到一组 但是我们要怎样发现这些功能区呢?
来看看这个最近的 Mac Catalyst App Swift Playgrounds 吧 想象一下你的朋友刚刚开始学习编写代码 他们打开了 Swift Playgrounds 并准备开始上第一堂课 不幸的是 他们被一个问题难住了 但是幸运的是 他们可以请教你 想象一下你用着电话和朋友隔空交流 你会如何帮助他们导航 app 呢? 你可能会问:“你有没有 在左边看到篇章列表? 你在哪一个篇章? 在你右边的世界视图里 你的字符字节在哪一个平铺图像上?” 又或者你想要教他们更多编写代码的知识 在这种情况下 你可以将中间分为两个功能组 顶部是你编写代码的编辑器 底部则是自动补全建议区 通过了解你将如何口头形容你的 app 你就可以更容易地识别这些应该 被囊括进辅助功能容器的功能区 另一个你应该使用辅助功能容器的时间 便是当它们具有相关类型或功能目的时 比方说 UINavigationBar、UITabBar UICollectionView 和 UITableView 都是一些标准的 UIKit 视图 默认情况下 它们都是 semanticGroup 类型的辅助功能容器 因此 如果你已经创建了 可以充当标签页栏或导航栏 并且是自己所定制的用户界面元素 请通过将其配置成辅助功能容器 以遵循预期的默认行为 现在来看看我们 Roasted Beans app 的一个例子 以了解改进自己的 app 导航体验有多么容易 从左边开始 我们有一个 UITableView 它是默认的 semanticGroup 类型的辅助功能容器 还记得我说过 辅助功能容器对 Mac Catalyst app 来说 是独立的辅助功能元素吧 这意味着 和其他任何辅助功能元素一样 我们建议你为容器 提供本地化辅助功能标签 在这个示例里 “Coffee list” 很适合 由于我们的 UITableView 维持了 选择某种咖啡的状态 我们可以通过在标签上 添加所选的咖啡来让这个标签变得更好 现在是暂停的好时机 我要提醒大家 在 app 里添加优秀的 辅助功能标签是很重要的 欲了解如何制作优秀的辅助功能标签 我希望你可以去观看我们 2019 年的演示 回到我们的 app 上 右边的“详细信息”视图又是什么? 这是一个保留着所有与所选咖啡 相关信息的功能区 “详细信息”视图还没有被分组 因为我们并没有使用 UITableView 或 UICollectionView 所以我们需要手动定义这些辅助功能容器 今天我将向你展示如何轻易地 将“可用性”这种用户界面制作成容器 在一个垂直的 UIStackView 里 我们会将它实施成一系列的 UILabels 这意味着用户需要导航至每一个元素 我们可以通过添加一个辅助功能容器 使导航体验变得更好 这样旁白用户就能快速导航完整个列表
在这里 仅需短短几行 我们就可以将 UIStackView 变成一个辅助功能容器 并给予它一个 可以形容整个分组的辅助功能标签
这是我们 app 的一些非凡提升 通过将这些相关联的元素分到一组 我们可以大大改进旁白用户 在 Mac Catalyst app 里导航的方式 因此我希望你能明白 在你的 app 里 添加辅助功能容器的重要性 这是你能做的一个小小改变 但是它却会大大改进 macOS 和 iOS 的导航体验
请确保你的辅助功能标签 具有形容该分组的简洁本地化标签 并包括了任何有关 该容器状态的重要详细信息
那么我今天要与你分享的 导航效率部分就到这里为止 我会将时间交回给 Eric 他会向你展示 如何测试你 Mac Catalyst app 的 辅助功能 谢谢 Nathan 为了改进你的辅助功能 app 体验 你付出了非常多的努力 所以现在就来聊聊 在 macOS 平台上进行的测试吧 当你把 app 从 iOS 带到 Mac 你会将 app 的内在想作 iOS app 可是外在想作 Mac app 因为它是在 macOS 平台上运行的 正是这样 当你从 UIKit 使用辅助功能 API 时 我们的团队会自动地将 你的 iOS 辅助功能代码转换成 macOS 你什么事也不用做 这样你就可以一直以 iOS 的角度研发 为了帮助你更好地了解内里的情况 我们已经改进了 Mac Catalyst app 的辅助功能检查器 而现在 当你的 app 在 macOS 上运行时 它会向你显示 iOS API
如果你还未使用过辅助功能检查器 我强烈建议你观看以下两个视频 即 “Accessibility Inspector” 以及 “Auditing Your Apps for Accessibility” 这两个视频能给予你完整的指南 了解辅助功能检查器能如何帮助你 横跨所有的 Apple 平台 搜索并修复 你 app 的辅助功能问题 现在就来看看如何使用辅助功能检查器 在 Mac Catalyst 上 检查 app 的辅助功能吧 如果我们使用检查器来检查单元的元素 我们可以看到元素 有着描述咖啡标题的正确标签 以及描述评分的正确值
我们也可以看到有一个新的 Catalyst 区 它显示了从 iOS 里带进的 辅助功能特点和容器类型 如果我们使用 Command-Control-Up 检查器就会检查单元元素的母元素 母元素扮演着容器的角色 通过检查元素类别 我们就可以确定 我们检查了正确的元素 在这个情况下 类别名称显示了 我们为 tableView 类别所转译的元素 所有来自 UIKit 的类别 最终都会被转译成 Mac 平台元素
除此之外 检查器还会让你知道视图的视图控制器 前提是你要拥有它 在这个场景里 它是来自 app 的 RBListViewController 要确保旁白念出容器 我们可以先确保 tableView 容器 拥有本地化咖啡列表的正确标签
它也拥有了正确的容器类型 即 semanticGroup
今年起 辅助功能检查器会告诉你 所有元素的自动化类型 在这个场景里 它是一个表 这样你就能确切地知道 如何在 XCUI 测试中找到它 以上是一个简单概述 说明了你能够使用辅助功能检查器 为 Mac Catalyst app 所做的一些额外之事 我们希望你获益良多
我们今天也涵盖了 Mac Catalyst 里 辅助功能的很多部分 做个总结吧 我们首先提到了 一个优秀的键盘 app 即是优良的辅助功能 app 要做到这点 你要确保 app 的控件 都能够通过键盘焦点来访问 以及添加键盘快捷指令
之后 我们向你展示了如何改进 Mac Catalyst app 辅助技术的导航体验 我们认识到了现有的 accessibilityContainerType iOS API 可以如何在 Mac 上带来重大改变 以及你应该如何在 app 里采用它 我们的最后一条建议是 使用辅助功能检查器 这是我们创建的一款有力工具 旨在帮助和你一样的开发者 谢谢你的加入 我们希望本次演示能够帮助你创建 具有更好辅助功能体验的 Mac Catalyst app
-
-
4:11 - Ensuring selection automatically triggers when focus moves to a different cell
myTableView.selectionFollowsFocus = true
-
6:01 - Creating a keyboard shortcut
extension AppDelegate { override func buildMenu(with builder: UIMenuBuilder) { super.buildMenu(with: builder) let shareCommand = UIKeyCommand(title: NSLocalizedString("Share", comment: ""), action: #selector(Self.handleShareMenuAction), input: "I", modifierFlags: [.command]) let shareMenu = UIMenu(title: "", identifier: UIMenu.Identifier("com.example.apple-samplecode.RoastedBeans.share"), options: .displayInline, children: [shareCommand]) builder.insertChild(shareMenu, atEndOfMenu: .edit) } @objc func handleShareMenuAction() { } }
-
7:20 - Responding to raw key codes
extension MyViewController { override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) { switch presses.first?.key?.keyCode { case .keyboardLeftGUI: // Handle command key pressed case .keyboardB: // Handle B key pressed default: } } }
-
15:45 - Adding accessibility labels to containers, such as UITableView and UICollectionView
tableView.accessibilityLabel = NSLocalizedString("Coffee list", comment: "")
-
15:50 - Making great accessibility labels that include state
extension RBListViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let data = tableData[indexPath.row] let label = NSLocalizedString("Coffee list", comment: "") let selectedLabel = NSLocalizedString("%@ selected", comment: "") tableView.accessibilityLabel = label + ", " + String(format: selectedLabel, data.coffee.brand) } }
-
16:45 - Adding accessibility containers to improve the navigation experience
let stackView = UIStackView() stackView.axis = .vertical stackView.translatesAutoresizingMaskIntoConstraints = false let locationsAvailable = viewModel.locationsAvailable let titleLabel = UILabel() titleLabel.font = UIFont.preferredFont(forTextStyle: .body).bold() titleLabel.text = NSLocalizedString("Availability: ", comment: "") stackView.addArrangedSubview(titleLabel) for location in locationsAvailable { let label = UILabel() label.font = UIFont.preferredFont(forTextStyle: .body) label.text = "• " + location label.accessibilityLabel = location stackView.addArrangedSubview(label) } stackView.accessibilityLabel = String(format: NSLocalizedString("Available at %@ locations", comment: ""), String(locationsAvailable.count)) stackView.accessibilityContainerType = .semanticGroup
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。