E48ce4d5ebf3232753b17fc3ca0f041a
【WWDC21 10121】为内容丰富的应用量身打造高效 VoiceOver 体验

作者:Ckitakishi,目前在日本从事 iOS 开发,对动画和图像感兴趣,也关注 Accessibility,正在为写出优美的代码而修炼。

审核:红纸,iOS 开发,周报编辑,就职于淘系技术部

本文基于 WWDC21 Session 10121 - Tailor the VoiceOver experience in your data-rich apps 创作

前言

Accessibility(无障碍辅助功能)从来都不是一个受关注的话题,而 Apple 对 Accessibility 的支持,一定算得上其最杰出也最令人敬佩的成果之一。

Accessibility 提供了非常多类型的辅助,包括视觉,听觉和其他动作交互。由于篇幅有限,本文将聚焦于视觉辅助,自然,不得不提非常优秀的内置屏幕朗读工具 VoiceOver(旁白)。作为本文作者,虽然近年才开始关注 Accessibility,但我私心希望你可以用一首歌的时间来读一读拙作,更期望越来越多开发者愿意投入精力为自己的 App 提供更好的无障碍支持。

这篇文章的着眼点非常细微,我们将会一同探索如何为一款拥有丰富内容,甚至内容过载的 App 提供更人性化的无障碍支持。

如何界定内容过载

不得不说,这是一个多少有些主观的问题,但也正因为这样,讨论才有意义。

从我个人的角度来看,关键信息淹没在冗杂内容中的场景就非常能代表信息过载。但判定标准总是因人而异的,为了顾及更多用户,我们需要追求尽可能在内容展示的量上达到平衡,亦或是提供多种可选方案来满足各式各样的需求。很容易联想到这样的设计,例如,截断长文,并在最后加上“显示更多”的按钮;另一个案例是小组件,一般我们都会提供多种尺寸,用户可以根据自己关注的重点来做出合适的选择。

通常,相同时间内听觉接收信息的效率是显著低于视觉的,所以在视觉上被认为并不多的内容量,转化为听觉之后完全可能发展为过载。对于如何解决这个问题,你一定想说,那我们按照以往的经验,添加“显示更多”按钮来减少内容的复杂度如何?思路完全正确,这正是我们奋斗的目标,只不过,这次并不是在 UI 上做出相应的改变,因为我们也不想牺牲视觉正常的用户的便利性。请允许我先保留一点神秘感,在之后的部分再来揭晓这个问题的答案。

到底如何界定听觉上的内容过载呢?我的建议是,闭上双眼,假设自己身处一些可能的场景,反复听一听,确认内容是否过于冗长或繁杂以至让你感到焦躁或疲倦。要注意,并不是只需要处理内容过载的情况,当内容足够多时,我们就最好警惕起来并开始着手解决这个问题。

VoiceOver 转子

还记得刚才留下的悬念吗?是时候揭开谜底了,实现听觉上“显示更多”的方法,正是使用转子。

转子是什么

转子可以理解为一种 VoiceOver 的增强导航系统,可以通过调节转子来更有效率地阅读页面上的内容。你可以通过阅读帮助文档来了解更多关于转子的事情:关于 iPhone、iPad 和 iPod touch 上的旁白转子

尽管我们今天并不会定义一个全新的转子,但我还是想稍微提及一点有关自定义转子的事情。在 iOS 10 之前,用户只能够使用内置的转子,而现在,我们可以为 App 打造更贴合功能的转子。在去年的 WWDC 中,VoiceOver efficiency with custom rotors 为我们阐述了如何实现自定义转子。

“更多内容”转子

在类型丰富的内置转子中,有一种叫做“更多内容”的转子,它能够为不同用户和场景提供不同复杂度的内容,以满足更多人在各种场景下的需求。

Accessibility 团队在 Session 中举了一个宠物犬列表的例子,每只宠物犬都拥有名字,类型,年龄,简介等字段,如下图所示。如果将这些字段都作为内容展示给听众,对于大部分用户而言,一段尚且还好,要听完十段这样的描述,真的需要极大的耐心和热情。

01-full

01-full

太多的内容会让人无所适从,而且从繁多的内容中提取少量自己关注的信息并不是一件轻松的事情。有一个概念叫做 DRIP (Data rich information poor) ,意思是数据丰富但信息匮乏,为了不让用户陷入这种困境,Accessibility 团队将名字和种类视为一只宠物犬最重要的特征,除此以外的内容都被放到了“更多内容”中。经过这次修改,用户再一次尝试 VoiceOver,就只会听到名字,种类和“更多内容可用”。就像下图显示的这样:

02-more-content

02-more-content

此时只需调节转子,用户便可以按需访问“更多内容”。

03-rotor

03-rotor

Accessibility Custom Content API

摩拳擦掌,终于要开始写代码了!帮助我们实现“更多内容”的是 Accessibility Custom Content API。其实它并不是 iOS 15 新增的 API,而是从 iOS 14 开始,但 WWDC20 中完全没有提及它(或许是为了等待 SwiftUI 的支持?),不过现在也为时不晚。另外,无论是 iOS, iPadOS 还是 macOS,所有平台都支持 Accessibility Custom Content API。

Accessibility Custom Content API 主要包括一个协议 AXCustomContentProvider 和一个类 AXCustomContent,前者提供了一个允许设置自定义内容的接口 accessibilityCustomContent,后者则是自定义内容本身,它包含一组标签和值。

那就开始吧。首先需要为 DogTableViewCell 设置无障碍标签,宠物犬的名字和种类可以被视为默认显示的内容,所以只需为 accessibilityLabel 赋予一个由名字与种类组合而成的值即可:

import UIKit
import Accessibility

class DogTableViewCell: UITableViewCell {
    override var accessibilityLabel: String? {
        get {
            guard let nameLabel = name.text else { return nil }
            guard let typeLabel = type.text else { return nil }
            // Tip:要记得加上逗号,否则 VoiceOver 语音的节奏和音调都会偏离你的预期
            return nameLabel + ", " + typeLabel
        }
        set { }
    }
}

之后则是要让 DogTableViewCell 遵循 AXCustomContentProvider 协议,并实现该协议唯一的属性 accessibilityCustomContent。要注意的是,labelvalue 的值都会被 VoiceOver 读出来,所以务必将它们设置为经过本地化的字符串:

class DogTableViewCell: UITableViewCell, AXCustomContentProvider {
    override var accessibilityLabel: String? { ... }
    var accessibilityCustomContent: [AXCustomContent]! {
        get {
            let notes = AXCustomContent(label: "Description", value: desc.text!)
            let popularity = AXCustomContent(label: "Popularity", value: popularity.text!)
            let age = AXCustomContent(label: "Age", value: age.text!)
            let weight = AXCustomContent(label: "Weight", value: weight.text!)
            let height = AXCustomContent(label: "Height" , value: height.text!)
            return [age, popularity, weight, height, notes]
        }
        set { }
    }
}

此外,AXCustomContent 对象中还有一个名为 importance 的属性,它的类型是一个可以用来控制内容输出时机的枚举:

``swift enum Importance: UInt { casedefault` // 需要的时候(切换到“更多内容”转子时)

top Created with Sketch.