包容性移动应用开发教程-全-

包容性移动应用开发教程(全)

原文:Developing Inclusive Mobile Apps

协议:CC BY-NC-SA 4.0

一、可访问性

我对可访问性的兴趣始于 2010 年,当时我是一家小型苹果经销商的经理。在科技零售行业工作的一个很大的好处是,你可以在科技之旅的不同阶段接触到各种各样的人。有些客户会问一些原创性的问题,需要进行大量的研究才能找到正确的答案。其他人以前从未接触过你或我可能称之为“计算机”的任何东西,并且是从最开始开始。

2010 年 6 月,苹果发布了 iPhone 4。从那以后,我开始注意到,到店里来的顾客中,使用英国手语(BSL)作为第一语言的人数显著增加。从与其中一些客户的互动中,可以清楚地看出这是为什么:随着 iPhone 4 的发布,苹果也发布了一个全新的功能——face time。

FaceTime 和其他类似的视频通话功能对我们使用 BSL 的客户来说是一个不可思议的可用性改进。FaceTime 只是现代智能手机和之前的座机电话众多辅助功能中的一个。

电话和无障碍创新

包容性技术的一个目标是为所有用户创造可比较的体验,这个话题我们将在第二章中再次讨论。亚历山大·格雷厄姆·贝尔没有包容性思维的优势来指导他的发明。因此,环境还没有准备好让他在设计电话时将可访问性放在首位。在接下来的几年里,我们必须努力增加辅助技术。许多技术已经被添加进来,比如我们很快会谈到的聋人电信设备。短消息服务(或 SMS)、语音识别系统和视频通话都是电话的进步。虽然这些辅助技术为残疾人带来了改善,但这些增加并不是无缝的。这款手机有着悠久的历史,它让有特殊需求的人更容易使用。今天,手机仍然引领无障碍创新。

自从将近 150 年前发明电话以来,我们大多数人已经能够按计划使用电话与隔壁房间、隔壁国家或全球各地的朋友、家人、企业通话。考虑一下,如果这还不是事实的话,你主要的,或者唯一的交流方式是手语。这立即使电话变得无用。电话是一项无处不在的发明,但它以一种单一的媒介——音频——来呈现内容。创新帮助我们这些听不见的人让手机变得更容易使用。

聋人用电信设备

聋人电信设备通常也称为 TDD、TTY、文本电话或迷你通信。它是一个 QUERTY 键盘和电传打字机显示器,发明于 20 世纪 60 年代,连接到固定电话(图 1-1 )。有了这些附加功能,TDD 允许语言或听力受限的人输入他们的对话。对话可以被引导到其他 TDD 用户,或者被引导到将对话转发给非 TDD 用户的运营商。

img/486920_1_En_1_Fig1_HTML.jpg

图 1-1

聋人用电信设备的一个例子

虽然 TDD 对许多人来说是一个必不可少的工具,但它并不等同于电话。如果你曾经使用过这些服务中的一种,你会知道与标准电话相比,这种交互更慢、更笨拙,就像你通过口译员用外语进行对话一样。TDD 是可访问的,但它不是包容性的。

视频通话

如今,FaceTime 是一项我们都认为理所当然的技术。我们大多数参与软件开发的人几乎每天都会使用某种形式的视频通话系统——Skype、Google Hangouts、Zoom 或任何其他系统。但是,十年前,能够以高质量的视频即时看到世界上任何地方的任何人,并与他们交谈是非常开创性的。然而,对于我们的聋人和重听客户来说,这不仅仅是突破性的。这对他们的交流能力是一种转变。签署对话的能力最终使电话成为一种可比拟的体验。

FaceTime 之所以没有成为苹果的产品,是因为苹果打算为手语用户提供一个优秀的辅助工具。苹果开始创造一款适合所有人的伟大产品。通过确保它为每个人工作,并在整个项目中考虑可访问性,苹果为一个特定的受众提供了超级服务。

移动创新

手机里充满了像 FaceTime 这样的辅助功能。谷歌助手和 Siri 快捷键降低了对准确性的要求。这有助于有学习困难的人。它还减少了帮助解决运动问题所需的触摸次数。屏幕时间和 Safari Reader 有助于最大限度地减少分心——非常适合注意力缺陷障碍患者或有精神健康问题的人,他们可以在焦点中找到解脱。听写、拼写纠正、预测文本、语音备忘录、提醒振动、第三方键盘和外部键盘支持都是辅助技术的例子。您很有可能每天都在使用这些工具,而从来没有考虑过它们是一个辅助功能。

考虑一下我们之前讨论过的用于聋人的电信设备。发明者需要某种方法来允许数字文本通过电线传输。他们的最终发明是调制解调器。没有调制解调器,我们的许多现代数字社会就根本不可能存在。下一次,当你带着一个滚轮箱或一辆童车出现在街道的拐角处时——下降的路缘最初是为了帮助轮椅使用者而设置的,这意味着你不必将沉重的物品举到街道的高度。强大的可访问性不应该是一个只有少数人使用的模糊特性。在最好的情况下,易访问性应该是你正在创建的产品或服务的一等公民,应该让每个人受益。

显然,无障碍不仅仅是让残疾人受益的事情。它为所有用户提供了更广泛的可定制性。虽然残疾用户可能受益最大,但是您的每一个用户都会从您给予可访问性的考虑中受益。在最好的情况下,可访问性是对您的每一位客户的包容。我们将在第二章中详细介绍这一点。现在,让我们对我们所说的残疾,尤其是数字环境中的残疾,有一个更深入的了解。

什么是残疾?

2019 年 1 月,一张照片在社交媒体上被广泛分享(图 1-2 )。这张照片展示了一个平常的女人走在市中心的街道上,忙着自己的事情。然而,这位女士有两个特点引起了脸书用户的评论。这位女士用的是智能手机,没什么不寻常的,但这位女士也有一根白色的拐杖来帮助她在城市中导航。

img/486920_1_En_1_Fig2_HTML.jpg

图 1-2

一个人使用拐杖和智能手机。发布到脸书,标题是“如果你能看到问题所在,就说我看到了”

自第一次世界大战以来,白手杖一直被用作帮助盲人和部分失明者的工具,许多人使用它来帮助他们通过感觉周围的街道来寻找障碍和线索,如触觉铺路,从而在建筑环境中导航。然而,手杖的主要用途之一,对于我们这些不使用它的人来说,可能不会立即显而易见;暗示就在手杖的颜色里。白色手杖的确是白色的。因为它们是白色的,对我们这些看到它的人来说,它们是一个明确的指示,持有它的人可能看不到我们。这给了我们一个提示,作为司机,我们要格外小心,作为行人,我们要确保给行人留出通过的空间。

也许这就是张贴到脸书的照片中的女士的情况。也许她视力不好,使用拐杖主要是为了向其他行人暗示她可能看不到他们。可能她确实使用她的拐杖来感觉她周围的建筑环境,因为她看不到远处的物品,但她的眼睛仍然具有更近的视觉,因此可以继续使用她的电话。

我不认为任何阅读这本书的人会像许多脸书用户一样,质疑这位女士的能力或缺乏能力,主要是因为你可能在移动领域工作,知道她可以做出大量的显示调整来改善她的体验,并允许她使用她的智能手机。也许她启用了大文本或缩放功能。也许她正在使用反转的颜色或增强的对比度。她甚至可能根本没有看屏幕——她可能正在使用屏幕阅读器,并且屏幕幕布已经打开,这只是使用手机时的自然方式。不去问别人,是不可能理解别人的经历的。如果你想真正了解别人是如何使用你的应用的,这正是我的建议——问他们。

所有这些都是在说——残疾并不像生活中的任何事情一样,仅仅是一种二元状态。不可能把世界分成两类——残疾人和健全人;或者,在我们的先例中,失明和视力正常。介于两者之间的是很大一部分人——从我们这些不得不戴老花镜完成特定任务的人,到我们这些因白内障、色盲和其他疾病而完全没有光感的人。随着时间的推移,视力障碍因人而异,所有的残疾也是如此。我相信我们都知道,并不是所有的残疾都是可见的。

那么,如果残疾是一个广泛的范围,我们如何定义它?强调我认为残疾的含义的最佳方式是使用世界卫生组织的定义。1980 年,世卫组织对残疾的定义如下:

在健康体验的背景下,残疾是指以人类认为正常的方式或在正常范围内从事某项活动的任何限制或能力缺乏(由损伤导致)。 1

世界卫生组织,1980 年

换句话说,他们将残疾定义为一个人的特征,一种使残疾人不同于我们其他人的东西,并且不能做我们合理期望“正常”人能够做的事情。我认为我们可以将残疾定义为“能力的限制或缺乏”但最后一部分出现了问题——“被认为是人类正常的范围。”这里的“正常”一词提出的问题比它回答的问题更多——什么是正常人?我们会期望这个虚构的正常人做什么?谁决定什么是正常的?如果我不符合正常的定义,我应该担心吗?所有这些问题的答案都很简单——没有这样的事情。人类是没有“正常”的。我不想听起来像学龄前儿童电视节目——我们都有自己独特的方式。

今天访问世界卫生组织的网站,你会看到一个更新的定义:

残疾不仅仅是一个健康问题。这是一个复杂的现象,反映了一个人的身体特征和他或她所生活的社会特征之间的相互作用。克服残疾人面临的困难需要采取干预措施,消除环境和社会障碍。 2

世界卫生组织

这个定义认为残疾不是人的问题,而是我们所建立的社会的问题。这个定义承认每个人都是不同的,有不同的能力、知识和技能,不同是正常的。因此,如果有人与我们社会的某个方面作斗争,这不是这个人的问题。相反,这是允许这种情况发生的文化问题。这一定义还承认,残疾不在于某人能否使用台阶进入建筑物。相反,它是关于我们赖以建立社会的系统,例如,如果我们让有学习障碍的人填写一个大表格,我们可能会让他们感觉如何,或者有心理健康问题的人可能会觉得被一个规定的系统所困。这被称为残疾的社会模式。

主要少数派

被认为有残疾的人是少数。但是他们一起构成了欧洲和北美最大的少数民族之一。在美国,估计有 27%的人有残疾,也就是 8530 万人。这使得残疾人数量相当于人口最多的三个州的总和。在英国,22%的人报告有残疾,接近 1400 万人。 4 全球有超过 10 亿人患有残疾,约占全球人口的 15%。 5 贵组织的设备支持政策很有可能涵盖市场份额远低于残疾客户总数的设备。

读到这里,我希望我已经让你相信了考虑不同能力的用户的重要性。因此,我敢肯定,你个人工作的可及性质量会高得多。然而,对残疾客户的最大影响将会发生,因为您的企业和您的同事与您一样坚信创造良好的无障碍体验。在接下来的部分中,我们将介绍实现这一点的方法。

易访问性的商业案例

最终,可访问性的理由很简单——这是应该做的事情。因为你的客户的能力而歧视他们是错误的。但是,我意识到,如果你是一个经理,或者你正在为一个业务经理的可访问性增加关注的案例,那么你需要包括其他的考虑。

美国国家残疾人组织估计,残疾人的可自由支配支出超过 2000 亿美元。 6 在英国,残疾人及其家庭的消费能力被称为“紫镑”紫色英镑被认为是企业的一项重要支出。有证据表明,不考虑数字无障碍的公司会因为让残疾人选择替代服务而失败。在英国,紫色英镑价值约为 2650 亿英镑或 3360 亿美元。 8

如果额外的 2000 亿美元市值对你的企业来说还不够,还有一个相当大的法律大棒,可能会导致巨额罚款和长期的声誉损害。

无障碍法律

与任何法律一样,各国在无障碍法律上的差异也很大。许多国家根本没有关于无障碍的法律。即使有法律,也往往只涉及政府或公共部门。有时,这些将是前数字法律通过公约粗略调整,以适应数字渠道。许多法规没有明确关注数字无障碍;相反,它们是更普遍的不歧视法律。你应该寻求法律建议,以确定哪些规则适用于你所在的市场,以及如何适用。

在这一节中,我将介绍两个主要地区的数字无障碍法律,我们大多数创建移动应用的人都必须遵守这些法律。本节旨在提供高水平的概述,而非法律建议,因此如果你认为这些法律可能适用于你的企业,我建议你寻求专业意见。

美国

美国拥有世界上最古老的无障碍法律之一。1990 年出台的《美国残疾人法案》(简称 ADA)。10

《美国残疾人法案》涵盖了政府确保供应商和政府提供的服务(如学校)无障碍的要求。私营部门包括有形的“公共住宿场所”——餐馆、剧院和你我的商店。ADA 对数字内容并不明确。但是支持《美国残疾人法案》的司法部坚持认为《美国残疾人法案》也足够宽泛,可以管理数字体验。在 2019 年的一个标志性案件中,达美乐披萨选择挑战这一论断。达美乐向最高法院提起诉讼,声称 ADA 不适用于他们的披萨订购应用。最高法院驳回了达美乐的诉讼。这开创了一个先例,意味着对移动可访问性的法律要求不是一个灰色地带。其他家喻户晓的名字,如国家篮球协会、网飞和碧昂斯都违反了 DOJ 法院对美国残疾人协会的裁决。

欧洲

2018 年制定的欧洲无障碍法案 11 是一部更加现代化的立法。EAA 将整个欧洲的无障碍立法作为参考。它也从美国反倾销协定中得到启示。目的是在全欧洲实现数字无障碍的无障碍要求标准化。

EAA 没有明确规定移动或数字服务的整体无障碍标准。它坚持定义类别的移动可访问性标准。其中包括电子商务、银行、与客运相关的软件服务,以及作为智能手机或智能手机操作系统一部分的软件。

要求因应用的业务类别而异。总之,一些共同的要求包括

  • 提供灵活的放大倍率、对比度和颜色。

  • 提供精细电机控制的替代方案。

  • 通过一个以上的感官渠道提供信息。或者以设备可以在替代的感觉通道中呈现的格式提供信息。

  • 为非文本内容提供替代内容。

  • 为辅助技术提供一致的互操作性。

倡导无障碍

提高应用可访问性的最佳方式之一是在组织内倡导可访问性。当 UX 给你发送新的设计,产品要求一个新的特性,或者你的团队正在提炼故事,所有这些都是提倡的好时机。

不要批评别人的工作。从我的经验来看,同事们确实很在意可及性,希望做得更好。有时他们缺乏这样做的知识和专业技能。当你看到可访问性已经被考虑时,支持工作;当你知道某样东西将很好地满足不同需求的用户时,祝贺工作。提出一些小建议,这些小建议将有助于改善体验。随着时间的推移,你会发现你的团队会接受这一点,并开始从易访问性优先的方法来考虑。

如果您的组织有多个面向客户的软件团队,您可以考虑建立一个易访问性倡导者网络。鼓励每组中的某个人参与进来。包括 UX、产品,也许还包括管理。通过空闲频道分享知识和问题。在某些情况下,找到培训预算是可能的,这提供了您可以向您的团队逐级传达的洞察力。根据你所学到的知识,考虑为其他同事举办培训课程。分享下一节中的一些活动有助于打破僵局。

展示可访问性

让同事和经理相信可访问性很重要,并让他们明白为什么它对您的客户很重要的一个有效方法是模拟您的客户可能正在经历的一些情况。通过这种方式,同事可以直接体验使用不同功能体验您的应用可能会是什么样子。不过,做这些练习时要小心。目的是让您对客户的体验有所了解。人们从这些练习中得到的有时是对残疾的怜悯和恐惧。

我收集了一些练习,提供了某些能力的粗略估计。对于这些活动,选择不同的日常智能手机任务。或者,为了使其与您的组织更相关,从您的应用中选择一个广泛使用的流程。要知道,你可能已经对你的应用更熟悉了,因为它是你做的。每次都让任务变得不同,以避免过于熟悉流程。

Simulating Blindness

iOS 有一个辅助功能,可以与其内置的屏幕阅读器 VoiceOver 一起使用,允许阅读器的用户维护隐私。这项功能被称为“屏幕幕帘”,它可以关闭显示屏上的图像,同时保持触摸屏处于活动状态。由于屏幕阅读器用户已经大声朗读了他们个人设备的内容,这可以防止他们无意中以可视方式共享个人内容。弱视或失明的用户可能意识不到私人内容是可见的,因此保持屏幕打开几乎没有好处。此外,禁用它可以增加隐私和电池寿命。Screen Curtain 为我们提供了一个绝佳的机会,让我们了解在没有视觉反馈的情况下使用智能手机会是什么样子。

在这项活动中,选择一项你可能用手机完成的日常任务。添加日历事件是一个不错的选择,或者为了使其与您的组织更加相关,您可以从您的应用中选择一个典型的流程。启用 VoiceOver–执行此操作之前,请阅读第六章中的“使用 voice over 导航”一节。然后用三个手指轻敲屏幕三次来启用屏幕幕帘。

现在,使用屏幕阅读器来导航手机并完成任务,而无需查看屏幕上的内容。虽然那些每天使用屏幕阅读器的人可能比一般的工程团队更熟练,但这应该会让你了解当语音是唯一的反馈时,智能手机使用的哪些方面更难。如果你是作为一个小组练习来做的,这也会让你了解嘈杂的环境是如何让屏幕阅读器的使用变得令人沮丧的。这相当于屏幕阅读器用户在你的屏幕上挥手。

股票 Android 没有一个等效的功能。但一些三星设备有一个名为黑屏的设置,这与启用对讲有相同的效果。

Simulating Attention Disorders

对于这项任务,你需要一些小气球或软球。从智能手机或应用的标准流程中选择一项你每天都要做的工作。你的任务是完成这个流程,同时保持气球或球在空中。虽然这种类比可能有点夸张,但这应该会让你了解到,对于有注意力缺陷障碍的客户,或者作为小孩子的父母,使用你的应用是多么容易。

Simulating Visual Impairments

来自英国的皇家国家盲人研究所制作了一款名为 EYEWARE 的应用。苹果应用商店 12 或谷歌 Play 商店都有免费提供。EYEWARE 与谷歌 Cardboard 配合使用效果最佳,但如果你手头没有的话,它也能工作。

RNIB 和有视觉障碍的人一起开发了 EYEWARE,以模拟有视觉障碍的人体验世界的感觉。该应用允许你从几种不同类型的损伤中进行选择——从色盲到白内障、青光眼等等,包括每一种的变体。尝试这些设置,并以不同的视觉水平完成您选择的一项或多项任务。例如,考虑一下色盲对颜色的使用是如何变化的,以及对于白内障来说,较大的文本是如何重要的。

摘要

  • 有时候,我们制造的技术会排斥有特殊能力的人。我们可以通过添加辅助功能来改善这些。但是这些附加品往往感觉就是附加品;它们不是无缝的。

  • 在下一章,我们将讨论数字包容。这是可访问性的最终目标——让技术对每个人都有宾至如归的感觉。

  • 残疾不是一种二元状态。我们都有能力,也有能力的极限。当我们建造的东西不适合拥有特殊技能的人时,残疾就发生了。

  • 在构建你的应用时,考虑人们的不同能力有一个很大的商业案例,一个 2000 亿美元的商业案例。这还只是在美国。您的业务也将受到国际无障碍立法的约束。如果你不遵守这些法律,你将受到巨额罚款和名誉损害。

我们将在下一章继续关注无障碍技术的背景。但是我们将不再把可访问性作为额外的考虑。相反,我们会考虑包容性,以及如何让所有用户都感受到自己是我们应用的一部分。

二、数字包容

在这一章中,你会发现一些关于包容性思维的历史以及这意味着什么。我们将涵盖支持这种思想的基本原则,并讨论在创建软件时应该考虑的事情,例如记住用户是真实的人的重要性。我们将涵盖任何一个软件工程师所能拥有的最重要的技能,它与为你的工作感到自豪密切相关。

包容性思维的历史

数字包容植根于建筑和产品设计。在创建移动应用的过程中,良好的设计是必不可少的。但是一个应用的可访问体验的很大一部分仍然是由我们这些不是设计师的人创造的。数字共融意识到,包括但不限于设计师在内的每个人都有责任鼓励更加无障碍的互动。

即使你不是一名设计师,我也认为值得我们花时间了解一下这个设计学院的背景。通过这种方式,我们可以了解所犯的一些错误以及修复这些错误的步骤。另外,如果你是一名工程师,你的工作就是将设计变成现实。因此,了解影响这些设计的作品将有助于你实现它们。

通用设计

数字包容植根于通用设计,这是一场始于 20 世纪 60 年代末的运动,它导致了我们今天在身边看到的许多常见的无障碍改进。建筑师和设计师罗纳德·梅斯创造了通用设计这个词。他用它来描述为尽可能多的人设计可用产品和建筑环境的概念,同时保持美学上的愉悦。

同为建筑师的塞尔温·戈德史密斯本人也是一名轮椅使用者,他通过倡导现在无处不在的下凹式路缘或路缘切割推进了通用设计的概念(图 2-1 )。路缘切口使轮椅使用者或行动不便的人能够比标准的凸起路缘更舒适地过马路。下降的路缘也是通用设计如何帮助尽可能多的人的最好例子。虽然最初的目的是提高轮椅使用者的机动性,但它也允许父母使用婴儿车,旅行者使用行李箱。

img/486920_1_En_2_Fig1_HTML.jpg

图 2-1

提供通向街道的无台阶通道的路缘切割

通用设计是建筑师、产品和工业设计师用来指导他们实现罗纳德·梅斯最初目标的一系列原则。1997 年,北卡罗来纳州立大学聚集了一些通用设计的主要倡导者,包括罗纳德·梅斯本人。他们一起定义了通用设计的七个原则。 1 这些是

  • 公平使用

  • 使用的灵活性

  • 简单直观的使用

  • 可察觉的信息

  • 误差容限

  • 低体力劳动

  • 接近和使用的尺寸和空间

我在这里非常简要地总结了这些原则的内容。我还简要介绍了每个原则如何适用于手机。这里的一些概念可能是新的,但是我们将在后面的章节中讨论所有的概念。

公平使用

让设计吸引所有用户。这个设计应该对具有不同能力的人有用并且有市场。避免隔离或侮辱任何用户。尽可能为所有用户提供相同的体验。如果你必须提供另一种体验,那就把它等同起来。考虑所有客户的隐私、安全和保障。

在移动环境中,这可能意味着避免使用不符合 WCAG 2.1(第三章)4.5:1 对比度准则的颜色。 2 此外,确保你的应用与对讲、画外音等辅助技术无缝配合(图 2-2 )。

img/486920_1_En_2_Fig2_HTML.jpg

图 2-2

VoiceOver 导航 iOS 的天气应用

使用的灵活性

您的设计适应了广泛的个人偏好和能力。提供选择,方便用户,并适应他们。

这在移动设备中的例子包括支持不同的文本大小。允许多种文本输入方法,如屏幕键盘、听写(图 2-3 )或外部键盘。两个平台都为您的客户提供各种定制设置;尽可能尊重这些。

img/486920_1_En_2_Fig3_HTML.jpg

图 2-3

谷歌的屏幕键盘允许通过听写输入

简单直观

无论用户的经验、知识、语言技能或当前的注意力水平如何,都要确保你的设计易于理解。消除不必要的复杂性。与用户的期望和直觉保持一致。适应广泛的读写和语言技能。以与其重要性一致的方式安排信息,突出最重要的信息。在任务完成期间和之后提供有效的提示和反馈。

在你的应用中坚持标准的设计语言,包括尽可能坚持系统提供的控件和约定。仔细总结信息,但如果客户想要了解更多信息,请允许他们深入了解细节。保持语言简单易懂。当你的应用需要用户输入时,你应该明确你需要什么信息以及在哪里。如果你的客户犯了一个错误,让他们很快改正。

可察觉的信息

你的设计应该有效地向你的用户传达必要的信息,不管环境条件或用户的感觉能力如何。使用不同的模式(图像、语言、触觉)来重复呈现基本信息。在前景和背景之间提供足够的对比——我们将在第三章中详细介绍。提供与感官受限者使用的各种技术或设备的兼容性。

为了在移动设备中满足这一原则,我们必须确保设备的屏幕阅读器或设备上可用的任何其他辅助技术可以访问所有内容。关于 Android 可访问性模型的第四章和关于 iOS 可访问性模型的第六章会给你一些工具。用多种方式展示你的内容。使用颜色、形状、图像、文本和布局的组合来使你的内容有意义(图 2-4 )。您的客户的屏幕阅读器会将文本内容转换为音频内容,但它不能将您提供的文本转换为图像。对于有价值的内容,可以考虑用其他方式提供,比如视频。

img/486920_1_En_2_Fig4_HTML.jpg

图 2-4

Android 结合了颜色、形状、文本和图像来传达意思

误差容限

将意外或无意行为的危害和不利后果降至最低。安排元素以最小化风险和错误:突出常用控件。提供危险和错误的警告。如果客户做出破坏性的改变,提前通知他们,并允许他们退出。如果你的顾客犯了一个错误,清楚地指出来,让他们毫不费力地修改。在承诺任何行动之前,让您的客户回顾他们的反应,并允许他们改变或回复。提供安全功能,这样破坏性影响就不会意外发生。

在移动应用中,这包括当你要求用户做出决定时,使用清晰简明的信息来说明每个动作的后果。给有害或破坏性的行为增加摩擦,并向客户提供反馈,告诉他们在做决定时会有什么样的结果。

低体力劳动

确保客户能够高效、舒适地使用你的设计,并尽量减少疲劳。尽量减少重复的行动和持续的努力。

你界面上的所有按钮都应该触手可及(图 2-5 ),尤其是在大屏幕设备上。提供应用常用区域的快捷方式,并在可能的情况下支持谷歌助手或 Siri 快捷方式。

img/486920_1_En_2_Fig5_HTML.jpg

图 2-5

常见功能的快捷方式位于屏幕底部,触手可及

接近和使用的尺寸和空间

最后一个原则最适用于物理项目。该原则规定,无论用户的体型、姿势或活动能力如何,都应该为接近、够到、操作和使用提供适当的尺寸和空间。使触及所有部件都很舒适,并适应手和把手尺寸的变化。

我们可以通过使我们的界面适应不同的屏幕尺寸和文本尺寸来满足这个指导方针。确保您的应用支持开关控制和开关访问,并允许键盘导航;此外,语音控制和语音访问(图 2-6 ),让您的客户几乎不用移动就能控制您的应用。任何交互元素都应该至少是 44px 的正方形,以便不坚持精细的运动控制。

img/486920_1_En_2_Fig6_HTML.jpg

图 2-6

Android 的语音访问功能允许在不触摸屏幕的情况下导航

这些通用的设计原则导致了许多我们认为是日常用品的产生。我们可能不认为这些产品中的许多是“可访问性”特性。考虑有声读物、自动门、高对比度标牌和柔性吸管。所有这些产品都是按照通用设计准则创造的。他们为许多能力不同的人服务,但也是对其他人的改进。这是我们应该在应用中努力实现的目标。我们的理想目标是创造一种包含所有人的体验。

通用设计考虑残疾问题,并考虑您可以通过修改设计来帮助有残疾的人,从而减少产品使用的障碍。减少障碍扩大了潜在用户的市场。然而,由于通用设计是在数字时代之前开发的,这种方法是为实体产品量身定制的。

包容性设计

包容性设计延续了通用设计的优势。它通常更适合数字交互。通用设计考虑到残疾,而包容性设计考虑到个人,倾向于更易接近的优先方法。包容性设计向前迈出了一大步,认识到残疾不是一种永久的二元状态,而是一种光谱。人们能力的不平等是很典型的,并且随着时间和环境的变化而变化。包容性设计通过将人们的需求视为永久的、暂时的、情境的或变化的来做到这一点。通过改善有特殊需求的人的体验并在第一时间考虑这些需求,我们可以将这种好处扩展到更广泛的人群,从而改善每个人在生命中某个时刻的体验。

数字无障碍顾问 Paciello Group 3 帮助定义了包容性设计的七个原则。他们设立了 inclusivedesignprinciples.org4来记录他们。这些原则是

  • 提供类似的经验

  • 考虑情况

  • 保持一致

  • 给予控制

  • 提供选择

  • 区分内容优先级

  • 增值

我在下面概述了这些原则以及它们如何应用于移动设备。

提供类似的经验

确保你的界面为所有人提供可比较的体验。允许人们在不破坏内容质量的情况下,以适合他们需求的方式完成任务。您可以在应用中提供音频描述或视频脚本。这将使你的原创内容易于访问。但这种替代是否抓住了原作的精髓和基调?移动交互有一种特殊的品质,让用户惊喜。你应该确保你的所有用户都能获得如此丰富的体验,即使他们使用的是辅助技术。

inclusivedesignprinciples.org 提供的一个例子是 Android live regions(图 2-7 )。实时区域是当内容改变时自动向辅助技术报告的屏幕区域。TalkBack 将读取该内容,而无需用户将焦点导航到该内容。iOS 没有实时片段功能,但您可以使用辅助功能通知自己创建一个。我们将在第八章中讨论这些。

img/486920_1_En_2_Fig7_HTML.jpg

图 2-7

“正确!”/“不正确的”屏幕区域是一个活动区域。TalkBack 会在内容发生变化时自动通知用户,而不会让用户失去焦点

考虑情况

人们在不同的情况下使用你的界面。确保你的界面给人们带来有价值的体验,不管他们的环境如何。

考虑在明亮的阳光下使用手机,音频和触觉反馈以及高对比度的颜色将有助于这种情况。在您的视频内容上提供字幕或隐藏字幕(图 2-8 )可以让父母在观看视频时降低声音,而不会打扰他们熟睡的孩子。

img/486920_1_En_2_Fig8_HTML.jpg

图 2-8

视频的字幕可以让人们在不打扰他人的情况下观看视频,也可以帮助有听力障碍的人

保持一致

使用熟悉的惯例,并始终如一地应用它们来促进熟悉和理解。这种一致性既适用于您的应用内部,也适用于您运行的系统。

不要在你的设计中重复发明轮子。当你通过使用 Android 和 iOS 为你提供的控件,站在巨人的肩膀上构建设计时,请随意对控件进行子类化,以添加你需要的功能或外观,但以这些为基础将会免费给你带来很多额外的东西。你的用户会有宾至如归的感觉,因为他们的交互会感觉很熟悉,苹果和谷歌已经为你提供了许多辅助功能。

给予控制

确保人们处于控制之中。您的客户应该能够以他们喜欢的方式访问内容并与之交互。不要禁止或禁用更改标准平台设置的功能,如方向或字体大小(图 2-9 )。此外,除非有办法控制,否则要避免未经用户同意的内容更改。

作为开发人员,有一些方法可以禁用一些辅助功能。例如,您可以创建一个不支持缩放文本大小的应用。这可能意味着你得到了一个像素级的完美设计,但它将极大地影响你的用户舒适地使用你的应用的能力。

img/486920_1_En_2_Fig9_HTML.jpg

图 2-9

iOS 动态测试风格的最小和最大设置。支持这些对您的客户来说非常重要,并且需要灵活的设计

提供选择

考虑为人们提供不同的方法来完成任务,尤其是复杂的或非标准的任务。完成一项任务的方法往往不止一种。你不能假设某人喜欢的方法是什么。通过提供布局和任务完成的替代方案,你为人们提供了适合他们和他们当时环境的选择。

一个很好的例子就是在 iOS 的邮件应用中删除邮件。我可以通过点击(图 2-10 )、滑动(图 2-12 )或长按(图 2-11 )来实现这一点。作为一名用户,这让我可以选择最快的应用使用方式。

img/486920_1_En_2_Fig12_HTML.jpg

图 2-12

通过滑动删除邮件

img/486920_1_En_2_Fig11_HTML.jpg

图 2-11

长按删除邮件

img/486920_1_En_2_Fig10_HTML.jpg

图 2-10

轻按删除邮件

区分内容优先级

通过在内容和布局中对核心任务、功能和信息进行优先排序,帮助用户关注它们。确定界面的核心目的,然后是实现该目的所需的内容和功能。当核心特性没有被清楚地展示和区分优先级时,界面可能很难理解。

逻辑地组织你的内容,用标题特征或角色来标记标题。当你启动电子邮件应用时,你不会看到可供选择的邮箱和文件夹列表。你会看到一个收件箱,因为这是最重要的功能。标准功能应该简单且始终可用。更复杂的动作应该是可能的,但不突出。让所有东西同时可用会导致界面混乱。考虑将高级控件放在辅助手势(如长按)之后。

增值

考虑功能的价值以及它们如何改善不同用户的体验。考虑语音、地理定位、摄像头和振动 API 等设备功能,以及与连接设备或第二个屏幕的集成如何提供选择。

这是包容性设计的领域,手机在这方面远远超过其他平台。移动设备充满了传感器和反馈模式。智能家居、屏幕或外部控制器等外部设备可以无线连接。两个平台上的拾取器控件都使用触觉来在事情发生变化时向用户提供触觉反馈(图 2-13 )。虽然这也是一种令人满意的体验,但对于有视觉障碍的客户来说,这是一种必不可少的反馈模式,可以帮助他们确定何时做出了改变。

img/486920_1_En_2_Fig13_HTML.jpg

图 2-13

在 Android 上设置闹钟。当在分钟/小时间隔之间移动时,提供触觉反馈

人物谱

微软是软件包容性设计的最大支持者之一。 5 微软推出了一款他们称之为人物角色谱的工具(图 2-14 )。人物角色谱帮助我们考虑包容性设计对使用我们软件的人意味着什么。

像包容性设计一样,我们将残疾分为永久性、暂时性或情境性。永久性残疾的一个例子可能是失去上肢。失去一只手臂会使许多日常交流变得困难,有些甚至是不可能的。对于这个人来说,改进你的应用可能意味着确保你所有的按钮都在握着手机的手的拇指容易触及的范围内。或者更好的是,增加语音控制功能。在这样做的时候,你也可以让你的应用对那些最近手臂受伤并使用吊带的人更有用。手臂受伤是暂时的损伤——至少我们希望如此。我们断臂顾客将在一个月左右恢复肢体的完全功能。有了这个改进,你也可以帮助新父母。新父母可能花很多时间用一只手,而他们用另一只手来抱或喂他们的婴儿,控制婴儿车,或握住一只手。在这个例子中,我们的新父母有情境障碍。一旦他们的孩子睡着或被交给另一个家庭成员,他们的第二条肢体就可以恢复使用。

对于这个例子,微软做了一些研究来强调这种思考方式如何给你的客户带来巨大的好处:

在美国,每年有 26,000 人患上肢缺失。但如果我们把有暂时性和情境性障碍的人包括在内,这个数字将超过 2000 万。

—微软包容性设计工具包手册 7

nine

img/486920_1_En_2_Fig14_HTML.jpg

图 2-14

角色光谱的例子

9

微软提供的这些例子也展示了视觉、听觉和语言角色谱的例子。盲人用户会感谢你考虑他们在没有视觉的情况下如何使用你的应用。这也将有利于最近做了眼部手术的人,或者正在开车而不能看设备屏幕的人。

考虑您的应用如何为聋人服务,将有助于患有耳部感染的人或在嘈杂环境中工作的人。一个使用非语言交流的人将会受益于同样的考虑,就像帮助像喉炎这样的暂时状况,或者像与带有浓重口音的人交流这样的情况。

New Spectrums

你的团队能创造人物角色谱的新例子吗?

考虑一下你的一个永久残疾的顾客。如果你为这些人改进你的应用,你会解决什么临时或情境场景?

考虑一个客户在野外的情况。你可以添加哪些功能来帮助这个用户?这对有暂时或永久损伤的人有什么帮助?一个患有焦虑症的客户怎么办?您可以做出哪些改进来为该客户提供更好的体验,这将如何惠及其他人?

数字包容

在一个敏捷的组织中制作软件不仅仅是实现一个设计师指定的屏幕。我们可以从包容性的设计方法中学到很多东西。但并不是所有这些都与我们这些不是设计师的人相关,而且很多并不直接适用于现代数字世界。

我们需要一种数字优先的包容性方法,一种构成更全面、更高层次信念的方法。一个认识到组织中每个人都有责任的策略,可以确保创建的服务适合尽可能多的人。拥有先进平台知识的开发人员是这一过程的重要组成部分。数字共融的一个重要部分是认识到残疾人虽然重要,但不是唯一想使用我们服务的少数群体。年龄、性别、教育、社会群体、收入和性取向等其他因素都会影响我们对技术的体验。我在下面总结了我对数字共融原则的建议。

移情作用的

考虑他人,认识到每个人都是具有不同知识、经验和能力的个体,并承认这使人们对你的应用的体验不同——可以说,这是数字包容性最关键的原则,我们将在自己的章节中讨论这一点。

环境形成的

并非所有使用你的应用或服务的障碍都来自人们的身体能力。包容就是把人的情况作为一个整体来考虑。这包括他们的技能,也包括其他因素:财务、性别、数字素养、心理健康和第一语言等等。要意识到所有这些因素都会影响人们使用你的应用的方式。

学会的

贵组织创建的软件反映了贵组织本身。想想一些社交媒体领袖的几乎自由主义的观点是如何导致几乎任何事情都可以发生的平台,包括有时的种族主义、选举干预和骚扰。组织中的每个人都可以在提高应用的用户可访问性方面发挥作用,并且有责任这样做。在下一节中,我们来看看我们所说的用户是什么意思,以及这些人是谁。

用户

在软件工程中,我们通常为我们的“用户”创造产品有时人们会引用数据可视化先驱爱德华·塔夫特的话,称人们为“用户”是我们和毒品贩子共有的习惯。不过,这可能也不是真的。我想,毒品贩子仍然称他们的顾客为“顾客”,因为他们就是“顾客”。真的只有警察和媒体才使用“用户”这个词我不打算监管你使用的语言,但是让我们来看看为什么我们应该对使用这个词有第二个想法。

个人

我不认为使用用户这个词有什么固有的错误;事实上,我在整本书中都使用了这个术语。问题是这个词经常隐藏了我们真正在为谁做软件——个人。你的每一个用户都是独立的个体,每个人都有自己的需求、能力、经验和对软件的要求。

使用“用户”这个词可能会导致陷入将用户视为一个群体的陷阱——一群在世界某个地方使用你的应用的人,一群你永远也不会遇见和了解的人。这种形式的“群体思维”导致了一种“一刀切”的解决方案,就像所有声称“一刀切”的事情一样,实际上没有人适合。

你最大的优点是别人都不是你。别人没有你的技能和经验。同理,你也没遇到过“正常”的人,因为没有人是正常的。每个人看待和体验世界的方式都不同,因为他们看待和做事情的方式不同。还记得那次你的应用出现了一个奇怪的 bug,因为有人以你无法想象的方式使用你的应用吗?我们都有过这样的经历。人们做不可预测的事情正是因为他们的想法和行为不同。对你来说不可思议的动作组合对使用你的应用的人来说可能是完全合理的。相反,对你来说显而易见的事情对一些使用你的应用的人来说却是不可理解的。

当创造任何东西给别人消费时,根据我们自己的经验为人们建造它是完全自然的。这样做的问题是,你或你办公室里的任何其他人都不会成为你正在创造的产品的主要消费者。虽然每个人都是不同的,但在科技公司办公室工作的每个人,很可能在某些关键方面比几乎所有其他人都没什么不同。如果你在科技行业工作,你很有可能非常了解科技,并且可能用过很多科技。也许你甚至喜欢使用它;这不是很多人的经历。

应用科学讲师瓦西里斯·范·格默特警告我们,认为我们是自己的用户是危险的。

在过去的 25 年里,我们主要是为设计[软件]的人设计[软件]。

——瓦西里·凡·盖斯特【8】

你的经历

想想你的朋友和同事,想想你的家人。希望你能和所有这些群体相处融洽,但也有细微的差别。

你的朋友很可能和你相似,因为你选择和他们一起出去玩,因为他们是你的朋友,我已经可以说他们会很棒。也许他们不从事技术工作,但因为你从事技术工作,他们很有可能拥有相当高的数字素养。他们通常和你年龄相仿,来自相似的背景,并且非常喜欢和你做的许多事情。

你的同事可能更加多样化,但是你们仍然会有一些不代表大多数人的共同点。你们都到了工作年龄,能够工作。你们赚的钱都差不多(和一般人群比)。而且你们都将对自己工作的行业有经验和兴趣。与你的家庭相比。我们大多数人都能想到至少有一个家庭成员不使用互联网,也许是一个有残疾的家庭成员,或者对他们来说英语不是他们的第一语言。你的家庭将会比你的朋友甚至你的同事组成一个更加多样化的群体。

考虑这三个群体以及他们的不同之处可能是你从个人经历中得到的最好的例子。但是把所有这些人放在一起思考,你最多只能得到几百个例子,来说明人们的生活是如何支配他们的技术知识的。世界由近 80 亿人口组成。因此,要获得对人们技术体验的代表性调查,你至少需要 1000 个例子。你只选择与你有某种联系的人的抽样方法不会通过审查。

如果我们以自己的能力为基准,我们做出的东西对某些人来说很容易,但对其他人来说却很难。

—微软设计 10

解决这个问题的最好方法是和真实的人交流。我们将在第十一章讨论用户测试。希望您的组织已经在进行某种形式的用户测试了。不要把这当成设计或产品的活动。工程学也能学到很多东西。

Your Network

这里有一个快速练习来强调为什么考虑你认识的人可能不代表你的客户。列出你最信任的五个人。这些人可以是同事、朋友或家人,你可以向他们寻求建议。在继续阅读之前,请这样做。现在把你自己添加到列表中。

现在,列出他们的一些特征。对每个人使用相同的特征。他们多大了?他们的性别是什么?列出他们的种族、性取向、就业状况、宗教和第一语言。记下你意识到的任何残疾。你可能还能想到其他特征的例子。在这里,诚实和保密你的答案是可以的。

看看你对每个问题的回答。我对大多数人的猜测是,这些特征看起来会很接近。没关系。这个练习的目的不是让你羞于扩大你的社交网络,只是想强调你身边的人可能比你想象的更亲近。

People Profiles

帮助整个团队将客户视为个体的一个好方法是创建个人资料。创造一个背景故事。考虑这个人想用你的应用做什么,为什么。想想这个人的环境、经历和能力。最重要的是,给你的人一个名字,用这个名字称呼他们。使用表 2-1 来指导您创建角色。

表 2-1

轮廓截面

| **人名** | 祝你选择名字愉快,但是一旦你创建了个人资料,就必须称呼这个人的名字 | | **年龄** | 给出一个大概的年龄范围 | | **性别** | 请记住,大约有 2%的美国人不认为自己是男是女 11 | | **直系亲属** | 他们有需要照顾的孩子或成人吗?他们结婚了吗,和家人住在一起,还是独自生活? | | **工作和收入** | 你的人可能是在职的,失业的,个体经营的,未充分就业的,或者在事业上非常成功的。也许他们是彩票中奖者 | | **残疾** | 全球约有 14%的人患有残疾。你应该把目标放在一系列反映不同残疾的档案上,而不是一个都没有 | | **背景故事** | 狂野一点。或者不是。请记住,此简介旨在代表一个真实的人,但请随意在此寻找乐趣 | | **这个人为什么要用你的 app?** | 你的应用能为他们做什么?这是一种选择,还是他们因为某种原因不得不用你的 app? |

神入

软件不是由冰冷的、不经思考的算法组成的。有时,这被用来为以软件的名义做出的错误决策进行无力的辩护。更重要的是,这降低了你作为开发人员的技能。它隐藏了这样一个事实,即创建软件是一门手艺,我们这些制作软件的人是真正意义上的手艺人。我们手工构建漂亮的软件,真正关心的是软件的内容和最终结果。我们总是在磨砺我们的技能,拓展我们的个人工具箱。

我想提出的是,一个软件工匠可以拥有的最伟大,也是最容易被忽视的工具之一是同理心——理解许多其他人,不像你,会直接受到你在创建应用时所做决定的影响的能力。这必须是数字包容的核心原则。同理心不是一项技能,你可以通过对一个项目有最多的承诺或在 GitHub 上有最多的明星来学习。这是一个工具,你只能通过对人和你的手艺产生真正的兴趣来提高。

很多书会告诉你成为一名伟大的工程师所需要的必备技能;这不是本书的预期目的。我发现了一个清单,我觉得这是成为一个真正的软件工匠所必需的技能:

  • 对感情和美学的开放

  • 与人打交道

  • 合作的

  • 工作生活平衡 13

然而,这个列表的作者从来没有打算把它们作为成为一个更好的工程师的指南。相反,恰恰相反。这些都是谷歌前雇员詹姆斯·达莫尔在他臭名昭著的“谷歌备忘录”中给出的例子。这份名单意在批评女性软件工程师。性别主题的基本读物 14 会告诉你这些例子是基于过时的、不可信的研究。

相反,对于任何从事软件工作的人来说,它们都是一套非常理想的技能。通过确保我们的团队拥有尽可能丰富的生活经验,我们可以利用这些经验来帮助我们做出明智的决策。如果你有一个男性团队,你就排除了 50%的人的经历。

我提到达莫尔是因为他关于多样性的错误主张在许多方面与本书背道而驰。在题为“弱化共情”的一节中,达莫尔指出,共情导致我们“偏爱与我们相似的人,并怀有其他非理性和危险的偏见。”我想让你从这本书里学到的恰恰与达莫尔所说的相反。我们应该强调同理心,因为它有助于我们认识和克服我们潜意识中对与我们相似的人的偏见。只有带着情感和对什么是对的深刻的个人理解,才有可能做到感同身受。随着同理心带给我们的经验范围的扩大,我们可以不带感情地去战胜这些挑战。

移情作为一种动力

所以,你很有同情心。你想有所作为。你想为残疾人解决问题。这是一个崇高的目标,但它是在帮助残疾人,还是让你感觉更好?当你说你想为残疾人修理东西时,想想你在暗示什么。残疾没有错,也不需要固定。

有很多恐怖的故事,软件以可访问性和残疾用户的名义变得更糟,而这些人应该心存感激。 15 注意不要把感同身受当成行动的理由,而是调查的理由。同理心不是真实经历的替代品;某人的生活经历应该比任何感觉或指导方针更能引导你。交互设计师 Marie Van Driessche 给出了以下建议:

请残疾人士与你同桌。大多数人对与残疾人交谈不感兴趣,他们更喜欢感同身受。但是同理心并没有帮助我们。

-玛丽·范·德里斯钦斯基第十六第三

残障人士希望有同等的体验。他们希望能够像其他人一样使用你的应用。易接近性不是一种让你感觉更好或获得你的品牌影响力的姿态。可访问性是实现软件包容性的工具。

偏见

我们都有无意识的偏见。我们已经进化成为一种生存机制,这是人类完全自然的一部分。因此,对抗无意识偏见可能会适得其反。对无意识偏见训练的研究表明,如果有的话,这使偏见更糟,并鼓励刻板印象。 17

最好的选择是承认并接受我们都有偏见,并利用它们来帮助指导你。确保你的团队尽可能多样化。这将有助于你的团队通过利用彼此的个人知识和经验,以你自己的偏见的形式,与尽可能多的人产生共鸣。通过这种方式,您可以建立一个更广泛的基础来检查在您的软件中做出的决策的后果。

快速移动并打破东西

总的来说,我希望我们最终能够摒弃导致软件行业大量不良事件的哲学——“快速行动,打破常规。”就连马克·扎克伯格 18 自己现在似乎也意识到这不是一种合法的经营方式,更不用说是一种以消费者为中心的业务了。我想提出一种新的理念,我们可以用它来制作软件,在这种理念下,我们以一种深思熟虑和感同身受的方式工作,来逐步改善尽可能多的人的体验。

摘要

  • 包容性思维植根于工业设计和建筑。但这并不意味着它的指导原则不适用于工程。

  • 可访问性是关于包容性。做出改变,将你的客户带入你的应用,而不是为不同能力的人创造不同的体验。允许您的客户定制适合他们的体验。

  • 记住你的用户是谁;他们不会永远像你、你的同事、朋友或家人一样。用户测试是了解人们如何体验你的应用的重要工具。确保你的参与者是多样化的。

  • 同理心是任何一个软件工程师的必备素质。这有助于你记住将你的用户视为独特的个体。

我们现在有了一个关于可访问性和包容性意味着什么以及是什么驱动了这些领域的思考的背景。让我们开始看看我们可以帮助不同的人使用我们的应用的实际方法。在讨论任何具体技术之前,我们应该看看我们的目标是什么。网页内容可访问性指南,或 WCAG,形成了一个明确的框架,说明我们应该对我们的应用进行哪些改进,以使它们更容易访问。

三、移动网络内容可访问性指南

有了网站内容可访问性指导方针这样的名字,人们很容易把它们写成不适用于移动设备。然而,这个名字是在整个 20 世纪 90 年代和 21 世纪初指导方针制定的时代的功能,最后一次重大更新是在 2008 年,当时手机还没有像今天这样普及。这与该倡议来自万维网联盟的事实相结合。这个名字也有点拗口,所以我们将坚持使用 WCAG 的常见缩写。

WCAG 1 就像任何一套指导方针一样,遵守 WCAG 教就像是一个复选框练习。可访问性实际上就是人们如何体验你的应用。但是除非你有足够的资源对大量不同辅助技术的用户进行广泛的测试,否则 WCAG 指南是目前为止你能得到的最好的指南。

W3C 将 WCAG 分成了四层。它们从四个首要原则开始——可感知、可操作、可理解和可靠。这些原则各有准则,接着是成功标准技巧。推荐最初是明确为网络而写的。但是大多数的原则指导方针都是通用的,确实可以转化为移动设备,即使它们的成功标准技术并不总是符合。最新版本 WCAG 2.1 于 2018 年获得批准,并涵盖了更多与移动相关的问题,如屏幕方向。

WCAG 自 2012 年起成为 ISO 标准,是全球许多无障碍法律的基础。它通常被用作法院决定数字体验是否可访问的基准,或者用于可访问性诉讼。因此,就法律而言,WCAG 非常适用于手机。

在这一章中,我不会完整地介绍每个指南;为此,我建议您亲自阅读当前的 WCAG 2.1 规范,可在 www.w3.org/TR/WCAG21 获得。相反,我将提供一些一般性的建议,告诉你如何在手机上遵守每一条准则,以及你可以使用什么工具来做到这一点。我们将在本书的后面详细介绍这些技术和工具。简而言之,我试图将专注于网络的成功标准技术转化为移动环境。W3C 对 Web 的详细指导比这本书的页数要多得多,所以为了简洁起见,我将保持高水平。W3Cs Web Accessibility Initiative 指导如何将 WCAG 应用于手机 2 在很大程度上,当 WCAG 指南提到“网页”时,我们可以假设这包括移动应用内容。

可知觉的

信息和用户界面组件必须以用户可以理解的方式呈现给用户。

—WCAG 2.1

第一个原则是可感知的,包括应用中的内容以及向客户提供这些内容的方式。用一句话来总结这个原则,我会用“替代方案”

回想一下我们在第一章对电话的讨论;按照最初的设计,电话只使用一种媒介,即音频。虽然这将涵盖大多数用例,但它立即排除了说话困难的人和听力困难的人。TDD 的发明是一个必要的选择,让处于这种情况下的人拥有和我们其他人一样的体验。同样,如果您提供的内容是纯视觉格式,如图像、屏幕阅读器不可用的文本或没有音频轨道的带字幕的视频,那么有视觉障碍的客户将无法访问这些内容。

替代文本

为任何非文本内容提供文本替代,以便将其转换为人们需要的其他形式,如大号字体、盲文、语音、符号或更简单的语言。

—WCAG 2.1

确保所有非文本内容都有适当的等效文本替代。这通常适用于有意义的图像。内容和非内容之间的区别在这里是至关重要的。为装饰图像提供可访问的描述性标签可以为用户提供更丰富的体验。但是注意不要分散你应用内容的注意力。

这两个平台都内置了向几乎任何对象添加文本替换的功能。我们在 iOS 可访问性模型和 Android 可访问性模型章节中详细介绍了每个选项。Android 使用contentDescription属性(列表 3-1 和列表 3-2 )。在 iOS 中,accessibilityLabel也会达到同样的效果(清单 3-3 )。这些方法中的每一种都有实现细节;详见第四章和第六章。

submitButton.accessibilityLabel = "Submit"

Listing 3-3Setting an accessibility label in iOS

submitButton.contentDescription = "Submit"

Listing 3-2Setting an Android contentDescription in code

<Button
    ...
    android:contentDescription="Submit" />

Listing 3-1Setting an Android contentDescription in a layout XML file

Alternative Text For Images

具有 UI 功能的图像(如按钮)必须有一个清晰的描述性文本替换标签,以便屏幕阅读器用户可以知道有一个可用的操作以及该操作的后果。

装饰图像需要更多的考虑。小的装饰性图像,比如吸引人们注意表格行中内容的图标,可能不应该包含替换文本。这增加了不必要的噪音,增加了导航所需的滑动次数,混淆了用户界面的顺序,导致用户失去上下文。

然而,更大的装饰性图像,如英雄图像,可能应该包含图像用途的文本描述,例如,一个勾形图标应该包含“你已经准备好了”的描述,而不是“勾形图标”这个标签不应该将焦点从页眉上移开,而是为那些试图在屏幕上定位自己的视力不佳的屏幕阅读器用户提供一个指示。如果屏幕阅读器跳过了屏幕的大部分区域,这些用户可能会认为屏幕的这一部分包含您没有完全设置为可访问的内容,并会尝试查找该区域中的任何内容。这将导致在 iOS 上一个恼人的“扣篮”声,如果你不能描述什么存在的话。可以把这想象成屏幕阅读器相当于“这一页故意留白”

基于时间的媒体

为基于时间的媒体提供替代品。

—WCAG 2.1

对你我来说,这意味着音频和视频。此外,它还包括任何具有时间成分的媒体,如动画。视频和音频应该有字幕或转录。优选地,视频应该包括音频描述,并且理想地,音频和视频都应该包括或具有手语选项。在第五章和第十章中可以找到更多关于媒体字幕和备选曲目的详细信息。

本指南不包括您作为文本替代物提供的视频或音频。如果您确实提供了这些选项,请务必标记出来。如果你不能让你的用户明白你的媒体是一种选择,那些不能使用它的人可能会感到被排斥。

适合的

创建能够以不同方式呈现的内容(例如更简单的布局),而不会丢失信息或结构。

—WCAG 2.1

适应性指南远离了内容,涵盖了如何在 UI 中呈现内容。

以清晰、有意义的顺序呈现内容。您可以通过良好的界面设计来实现这一点,但是在测试时要注意启用屏幕阅读器。对于视力正常的用户来说,它们经常以不自然的方式在屏幕上导航。例如,考虑一个带有大的英雄图像和标题的屏幕。你的眼睛通常会浏览图片并被标题吸引。除非您指定标题应该是最初聚焦的项目,否则屏幕阅读器将首先聚焦于图像的 alt 文本。

以多种感官特征呈现信息。例如,不要只依赖颜色,而是要结合颜色、形状、大小、位置或声音等属性。你可以使用任何或所有这些模式来推断意义。

输入字段和其他控件应该标识该控件的目的或动作。使用我们之前讨论过的 Android 上的contentDescription属性和 iOS 上的accessibilityLabel属性来实现这一点。在 iOS 上,可访问性特征是告知辅助技术及其用户控件如何工作的强大工具。如果需要,iOS 上的accessibilityHint(列表 3-5 )或 Android 上的hint(列表 3-4 )可以给出额外的上下文。关于 Android 可访问性模型的第四章和关于 iOS 可访问性模型的第六章提供了关于如何使用它们的更多细节。

sendButton.accessibilityHint = "Sends your message."

Listing 3-5Setting an accessibility hint in UIKit

<EditText
...
android:hint="Email Address" />

Listing 3-4Setting a hint in an Android layout XML file

Providing Hints

当使用描述、标签或提示来提供上下文时,很容易给出过多的背景,例如,在文本字段上添加“双击以编辑”作为提示。

记住:屏幕阅读器用户在使用屏幕阅读器界面时会像你或我在使用可视界面时一样自然。虽然导航中的这些差异对您来说可能是新的,但没有必要解释它们。这样做增加了不必要的噪音,而且可能会让你的用户觉得你高人一等。

WCAG 2.1 的一个新的补充是手机日益流行的结果。用户界面不应局限于一个方向,而应允许旋转到任何设备支持的方向。这适用于除非明确的方向是一个明确的目的的要求,如音乐键盘需要景观。

可区别的

让用户更容易看到和听到内容,包括将前景与背景分开。

—WCAG 2.1

可区分指南涵盖了用户如何区分应用中的元素。该指南包括对比度、文本大小和行距、背景音频等基本规则。一些残疾,如色盲,可能会妨碍我们区分元素的能力。因此,坚持这些指南并在需要的地方允许定制是非常必要的。除非你的布局需要,否则避免二维滚动,因为这可能会触发对运动敏感的人。启用一些辅助技术进行控制可能会很有挑战性。

本指南还就以下要素提供了建议。

颜色

你在任何应用中使用颜色都是至关重要的。调色板的正确选择是让你的应用与众不同的重要因素。使用得当的颜色可以微妙地暗示意义,有助于理解。然而,请记住,不是每个人对颜色的体验都是一样的;因此,颜色不应该是识别元素或传达意义的唯一方式。将颜色与文本、形状或动画结合起来。例如,黄色警告三角形不仅仅被称为“黄色警告”——三角形部分对其效用至关重要。

文本颜色和背景颜色之间的对比度是一个重要的考虑因素。大文本(定义为最小 18 磅或 14 磅粗体)的最小对比度应为 3:1。较小的文本的对比度应为 4.5:1。理想情况下,文本的对比度应该是 7:1,较大的文本应该是 4.5:1。我们将在第十一章介绍如何检查色彩对比度。

有些人,如患有阅读障碍、Irlen 综合症或色盲的人,可以从自定义背景颜色的功能中受益。如果你想让你的应用的可访问性更上一层楼,这将是一个很好的补充。

声音的

应用中任何持续时间超过 3 秒的音频,包括视频中的音频,都应该有控件。这些应该允许暂停、停止和调节音量。音量控制应该独立于系统控制,以便让您的客户保持他们选择的系统级别。

任何语音音频都应该避免背景声音。或者,允许禁用背景声音,或者让背景声音比语音声音至少安静 20db(至少四倍)。背景噪音会影响注意力障碍的人;对于一些有听力障碍的人来说,如果他们的听力失真,这还会使前景音频难以确定。

文本

文本的大小应该是字形大小的两倍,而不会丢失内容或功能。确保您支持系统,两个平台上的动态文本大小将满足这一要求。避免使用文本图像,因为它们不支持屏幕阅读器或文本大小调整。如果必须使用包含文本的图像,请确保在图像中添加包含文本的屏幕阅读器可访问的标签。使用 Android 上的contentDescription属性或 iOS 上的accessibilityLabel属性来完成此操作。

设置文本格式时,要做到以下几点:避免完全对齐,文本行数最多 80 个字符,段落间距为行距的 1.5 倍。通过提供更改文本和背景颜色的机制,让您的用户根据自己的需求进行定制。

可操作的

用户界面组件和导航必须是可操作的。

—WCAG 2.1

正如这个原则的名字所暗示的,operable 涵盖了你的应用是如何工作的。这包括使用客户可能选择用作替代输入或输出机制的任何辅助技术。

键盘可访问

从键盘上使用所有功能。

—WCAG 2.1

确保您的应用可以使用外部键盘导航。确保你的应用没有键盘陷阱——一个可以通过键盘聚焦但只会通过其他方式失去焦点的控件。当使用屏幕阅读器导航界面时,同样的情况也会发生。测试应用时,请确保浏览到启用了屏幕阅读器和外部键盘的每个控件。

两个平台都支持键盘导航,但是每个平台的实现都有不同的不足。参见第五章和第十二章了解更多信息。

期限

为用户提供足够的时间阅读和使用内容。

—WCAG 2.1

出于许多原因,时间限制可能是可取的:释放产品或资源以防止拒绝服务攻击,或者通过限制授权令牌的未授权使用来增加用户的安全性。然而,时间限制会给一些人造成障碍。使用辅助技术导航应用可能非常耗时,尤其是在可访问性设计不符合标准的情况下。此外,患有焦虑症的人会发现时间限制非常有压力。

如果没有严格的时间要求,尽可能避免时间限制。如果您确实包含了时间限制,请考虑为您的用户提供一种选择限制或延长限制的机制。在限制即将到期之前发出警告,并提供重置或延长的选项。如果需要重新进行身份认证,请允许您的客户在其身份认证过期之前从他们停止的地方继续。

此外,本指南还提供了移动、闪烁滚动或自动更新内容的规则。应用户要求,提供暂停、停止或隐藏这些功能的机制。

癫痫发作和身体反应

不要以已知会引起痉挛或身体反应的方式设计内容。

—WCAG 2.1

任何闪光或闪烁不应该超过每秒三次。众所周知,这会引起癫痫患者的身体反应。此外,您应该为客户提供禁用动画的选项,除非这对您的应用的功能至关重要。在 iOS 上,要听isReduceMotionEnabled属性。

可操纵的

提供帮助用户导航、查找内容和确定位置的方法。

—WCAG 2.1

这里假设你是一个视力正常的用户,当你看到一页内容时,你的眼睛和大脑会下意识地一起工作,为你建立一个可用的心智模型。这种无意识的技术让你可以在内容之间瞬间跳转,找到现在对你最重要的内容。出于这个原因,数字广告试图尽可能分散注意力。如果广告没有分散注意力,你的大脑可能会判断它不重要,甚至在你没有意识到的情况下跳过。如果你没有完全看到,这是一个工具,你不能从中受益。你将依靠额外的线索来充实自己的内容。

确保屏幕阅读器和其他辅助技术能够以逻辑顺序导航你的应用是至关重要的,确保内容与有意义的标题分开。有时,屏幕阅读器的导航顺序可能与您的眼睛自然看到的顺序不同。有可能以多种方式实现正确的、语义的屏幕阅读器顺序。为屏幕阅读器设置一些隐藏的元素,调整内容顺序或屏幕阅读器的主要焦点,或者使用语义视图都是实现这一点的技术。我们将在本书的后面部分讨论这两个问题。

每个屏幕都应该有一个屏幕阅读器可以访问的标题,并且你应该这样标记任何内容标题。这允许屏幕阅读器用户跳过有意义的标题,类似于我们上面讨论的略读。

你应该努力让所有的按钮都有独特的、有意义的文本标签,这些标签不需要任何额外的上下文就可以使用。

输入模式

使用户更容易通过键盘以外的各种输入来操作功能。

—WCAG 2.1

如果你的应用利用设备的内置加速度计来确定你的应用的运动和控制功能,为那些可能难以控制精细运动的人提供一个替代选择。

点击目标应至少为 44 像素见方。Android 的指南推荐最小 48px 的正方形。任何更小的尺寸都难以准确按压,并且会导致意外输入和客户失望。

该指南还涵盖了可用于控制应用功能的触摸手势。考虑到不可能所有用户都准确地执行您指定的手势,所以提供替代方案。iOS 允许您将元素(不限于按钮)标记为具有辅助功能操作。这些可以由用户使用他们的辅助技术直接触发。

可理解的

信息和用户界面的操作必须是可理解的。

—WCAG 2.1

WCAG 的第三个原则——可以理解——旨在提高你的应用及其功能的清晰度。这个原则涵盖了应用的内容、设计和行为。

易读的

使文本内容具有可读性和可理解性。

—WCAG 2.1

尽可能避免在你的文章中使用习语、行话和缩写。对于任何你使用的,提供一个机制给你的用户来决定他们的定义。理想情况下,目标是任何书面内容都可以在初中教育水平上理解。 3

可预言的

让网页以可预测的方式显示和操作。

—WCAG 2.1

设计和操作的一致性对于本指南至关重要。没有两个外观相同的控件应该有不同的功能。反之亦然;两个功能相同的控件应该具有相同的外观。

上下文的任何变化——移动或改变控件或内容的含义或行为,如导航到新屏幕——都不应在没有警告的情况下发生。这些应该只在用户请求时发生,比如说,通过按下一个按钮。或者你应该通知你的客户将会发生一些事情,也许是关于一个装载旋转器。对于一些有心理健康问题或学习困难的客户来说,未能让用户为环境的变化做好准备会导致焦虑。此外,视障用户可能没有意识到上下文已经改变,从而使得新内容的含义不清楚。

输入辅助

帮助用户避免和纠正错误。

—WCAG 2.1

当通过表格从客户那里收集数据时,任何错误都应该清楚地突出显示。提供导致错误的原因以及如何纠正错误的指示。每个字段都应该向用户提供关于其用途的清晰说明。

理想情况下,任何表单都应至少提供以下特征之一:

  • 可逆的

    可以逆转数据的提交。

  • 检查

    输入的数据在提交前经过验证。您的客户有机会纠正任何错误。

  • 确认的

    在最终完成提交之前,让您的客户有机会检查、确认和更正输入的任何数据。

粗野的

内容必须足够强大,能够被...辅助技术。

—WCAG 2.1

最后一条原则只包含一条指导方针。基本原则是,你的应用应该与用户选择的平台上的任何辅助技术兼容。

兼容的

最大限度地兼容当前和未来...辅助技术。

—WCAG 2.1

控件的用途应该由用户选择的辅助技术来决定。您可以使用 iOS 上的可访问性特征来实现这一点。例如,任何充当按钮的控件都应该标记为按钮。这允许所使用的辅助技术决定如何处理该元素。

摘要

  • 万维网联盟的网页内容可访问性指南规定了衡量应用可访问性的规则。

  • 这是一个国际标准,并形成了决定您的应用是否可访问的法律框架。如果你不符合 WCAG 的标准,你就有法律纠纷的风险。如果你担心,找一个 WCAG 专家来检查你的应用,并确定哪些准则适用于你以及如何适用。

  • WCAG 是来自用户和专家的资源丰富的研究的结果,这些人真的知道什么对可访问性最好。用他们丰富的经验来指导你。

  • 你的应用应该是可感知的,可操作的,可理解的,健壮的。

我们已经介绍了应该考虑哪些因素来提高应用的可访问性,并简要介绍了为什么应该考虑这些因素。现在让我们来看看每个平台为您提供的符合这些准则的工具。首先,我们从 Android 开始。

四、Android 可访问性模型

与平台的其他部分一样,Android 可访问性是高度可定制的。可访问性的很大一部分是关于可定制性的,所以 Android 在这方面有巨大的优势。如果 Android 没有一个适合特定需求的系统设置,那么任何开发者都可以创建一个定制的可访问性服务来满足这个需求。我们将在后面的“查看可访问性树”活动中粗略地看一下这个。由于手机和可用软件版本的差异很大,为了避免疑问,我使用的是运行 Android 10 的谷歌 Pixel 设备。

Google 的设计系统 Material Design 1 贯穿于 Android 系统 app 中,是你创建 app 的基础。谷歌开发材质设计时考虑了高标准的可访问性,并遵循用户界面设计的最佳实践。使用 Android 的内置控件(图 4-1 ) 2 )并遵循材质设计原则将意味着你的应用是一致的——不仅与 Android 系统一致,还与你的应用的其他部分一致。 3 这将帮助您的所有用户在使用您的应用时有宾至如归的感觉,并帮助您保持高水平的可访问性。

img/486920_1_En_4_Fig1_HTML.jpg

图 4-1

material.io 中的材质设计组件

Material design 的指南很好地介绍了如何使用 Android 的工具来创造无障碍体验。通读谷歌关于材质设计原则 4 和材质设计可及性 5 的介绍,以获得最佳实践。材质设计指南中的大多数信息都与制作 Android 应用的任何人相关,而不仅仅是设计师。

可访问性树

在我们介绍 Android 上可用的可访问性特性之前,让我们先了解一下 Android 可访问性模型是如何为辅助技术服务的。当使用 Android 应用时,您将熟悉 Android 从 XML 中扩展出来的控件和视图。创建一个你可以与之互动的用户界面。但是如何将这些视觉元素转换成辅助技术可以使用的格式呢?

Android 创建了一个被称为可访问性树的东西,它是呈现在屏幕上的元素的分层表示。辅助服务可以使用此辅助功能树来确定如何显示信息。Android 对你的用户界面以及如何向辅助技术表示做出了一些合理的假设。大多数情况下,这将提供一个可访问的功能体验。有时你可能需要调整这个树来呈现一个更好的体验。本章将为你提供实现这一目标的工具和技术。

辅助功能节点

Android 以可访问性节点树的形式向可访问性服务提供数据。辅助功能节点是屏幕上元素的表示。它们包含关于辅助技术可以执行的元素和操作的内容和元数据。

辅助功能节点和屏幕元素之间的区别是至关重要的。Android 并没有通过屏幕元素来呈现辅助技术,而是一个代理对象,包含与可访问性相关的信息。一旦可访问性服务接收到一个节点,这个节点就是不可变的。对视图的更改不会透露给可访问性服务,直到该服务下次请求该节点。

“辅助功能”节点包含对辅助技术有用的元素信息,例如,文本和描述等属性,以及元素是否可滚动或是否还包含标题等信息。元素在屏幕上的位置及其层次结构的细节也是如此。

Viewing The Accessibility Tree

Android 允许任何开发者创建可访问性服务。该服务可用于在屏幕上呈现内容、控制屏幕或设备,或者您可能期望辅助技术执行的任何其他任务。我们不会在这本书里讨论创建这样一个服务的来龙去脉,尽管 Android 确实提供了一个指南。我们将触及一些基础知识来说明可及性树是如何工作的。

如果你还没有,克隆这本书的 GitHub repo。导航至文件夹练习 4-1。在 Android Studio 中打开这个示例代码,然后单击 Run。

在您的设备或模拟器上,打开“设置”并找到“辅助功能”。在顶部,您应该会看到新的辅助功能服务列在下载的服务下面(图 4-2 )。打开服务,并启用它。

img/486920_1_En_4_Fig2_HTML.jpg

图 4-2

我们设备辅助功能设置中的新辅助功能服务

辅助功能服务将在屏幕顶部覆盖一个“获取辅助功能树”按钮(图 4-3 )。

img/486920_1_En_4_Fig3_HTML.jpg

图 4-3

“获取可访问性树”按钮覆盖在我们屏幕的顶部

在 Android Studio 中打开 Logcat 标签。单击新的“获取可访问性树”按钮。您将看到打印的一组输出(图 4-4 )。这些是屏幕上所有当前元素的文本描述和标签。尝试导航到不同的屏幕,并再次点按该按钮,以查看以不同布局呈现给辅助功能服务的内容。

img/486920_1_En_4_Fig4_HTML.jpg

图 4-4

Android 应用启动器的可访问性树输出

有关可访问性服务可用属性的完整列表,请查看 Android 文档。 7 考虑查询不同的值(清单 4-1 )比如isHeading,看看每个屏幕如何报告自己的可访问性。例如,请参见下面的清单。

if (node.isClickable) {
    Log.d("NODE", "is Clickable")
}

Listing 4-1Printing to the console if an element is clickable

辅助功能 API

Android 的可访问性 API 负责管理呈现给可访问性服务的视图。Google 在创建他们的标准控件时做了一些合理的假设。坚持使用 Android 类的控件,你的可访问性就没问题了。但是有时候你需要做一些调整来改善我们呈现给可访问性用户的内容。当您创建自己的自定义控件时尤其如此。如果你改变了 Android 的内置控件,你必须确认你没有删除任何辅助功能。我们将看看您可能需要考虑在这里设置的一些属性。

内容描述

对于像 TalkBack 这样的向用户呈现元素的辅助技术,该技术需要文本表示来呈现。默认情况下,contentDescription被设置为视图的文本值。对于没有文本值的元素,或者辅助技术提供的文本值不起作用的元素,您应该设置contentDescription值(列表 4-2 和列表 4-3 )。

contentDescription应该是一个简洁的、描述性的标签。通常一个词就足够了,比如“提交”不要包含诸如“按钮”这样的控件类型,因为 Android 会为您添加。不要重复描述,保留这些独特的辅助导航。

submitButton.contentDescription = "Submit"

Listing 4-3Setting a contentDescription in code

<Button
    ...
    android:contentDescription="Submit" />

Listing 4-2Setting a contentDescription in a layout XML file

与所有可以在布局文件中设置的属性一样,该值也可以在设计模式下在属性窗格中设置,如图 4-5 所示。

img/486920_1_En_4_Fig5_HTML.jpg

图 4-5

在“属性”窗格中更改属性

对于可访问性很重要

如果一个元素纯粹是装饰性的,比如一个伴随文本的图标,那么将这个元素添加到你的可访问性树中意味着对对讲用户的额外的不必要的点击。在这种情况下,我们不应该向辅助技术提供这种元素。你可以通过设置importantForAccessibilityno(列表 4-4 和列表 4-5 )来实现。或者,如果通常隐藏的元素提供上下文,您可以将该值设置为 yes。

textIcon.importantForAccessibility =
      IMPORTANT_FOR_ACCESSIBILITY_NO

Listing 4-5Setting a view inaccessible in code

<TextView
     ...
     android:importantForAccessibility="no" />

Listing 4-4Setting a view inaccessible in XML

暗示

提示值是元素的文本表示,独立于元素的文本值(清单 4-6 )。Android 以不同的方式使用这些值,这取决于您的元素是否有另一个文本值。

对于没有文本值的元素,提示显示为占位符值。提示也被 TalkBack 作为文本标签读取。这对于EditText元素向用户提供应该输入什么信息的提示很有用。出于这些目的,您的提示应该简短且具有描述性,遵循与上面的contentDescription相同的队列。

对于具有文本值的元素,不显示提示。TalkBack 读取元素文本值或contentDescription和元素类型后的提示。如果提示有助于用户浏览您的 UI,您可以使用它来为用户提供更多的上下文。例如,一个理想的用途是对按下按钮的后果进行简短描述。带有文本“发送”的按钮可能有“发送您的消息”的提示这使得将要执行的操作和预期的结果变得很清楚。作为编写提示的一般指南,想象一下向一个朋友解释这个控件是做什么的。

<EditText
    ...
    android:hint="Email Address" />

Listing 4-6Setting a hint in a layout XML file

可访问性标题

当使用 TalkBack 导航屏幕时,没有视力或视力很差的用户通过在屏幕上从左向右滑动来导航。TalkBack 从左上到右下依次关注屏幕上的每个元素。对于有大量内容的屏幕,这意味着你的客户需要浏览大量不相关的内容才能找到他们想要的信息。这样标记你的标题(列表 4-7 和列表 4-8 )允许对讲用户跳过内容浏览标题。

headingLabel.isAccessibilityHeading = true

Listing 4-8Setting an element as a heading in code

<TextView
        ...
        android:accessibilityHeading="true" />

Listing 4-7Setting an element as a heading in a layout XML file

最小尺寸

所有交互式元素,如按钮、复选框、开关和其他控件,必须至少为 44 个相对像素的正方形,以符合 WCAG 指南。Android 建议使用最少 48 个相对像素。

Android 提供了一种简单的方法来确保始终如此。无论设备或用户界面的其他部分如何,都将保持这种状态。针对任何交互控件设置minWidthminHeight值(列表 4-9 和列表 4-10 )。养成习惯,让它成为你添加到 UI 中的任何控件的标准属性。

imageButton.minWidth = 48
imageButton.minHeight = 48

Listing 4-10Setting a minimum size for an image button in Kotlin

<ImageButton
      ...
      android:minHeight="48dp"
      android:minWidth="48dp" />

Listing 4-9Setting a minimum size for an image button in XML

的标签

假设我们向客户展示一个表单,其中有一些EditText字段供他们填写。我们的客户如何知道在哪个字段输入什么数据?一种常见的方法是给我们的EditText添加一个提示值。Android 将此显示为该字段的占位符(图 4-6 )。

img/486920_1_En_4_Fig6_HTML.jpg

图 4-6

编辑带提示的文本控件(左)和带内容的同一控件(右)。注意内容提示是隐藏的

当该字段是空的时,这很好地工作。一旦该字段输入了值,该占位符就消失了(图 4-6 ),并且不再清楚该字段的用途。对于注意力或记忆力有问题的人来说,这是一次糟糕的经历。labelFor允许我们将一个TextView元素绑定到一个EditText或其他控件上。无论控件处于何种状态,该标签都保持可见(图 4-7 )。不幸的是,这种技术确实给对讲用户增加了额外的不必要的刷卡,但总的来说,我相信使用labelFor属性会给你的客户带来更多的好处,而不是带来不便。

img/486920_1_En_4_Fig7_HTML.jpg

图 4-7

带有关联标签的 EditText 控件(左)。即使输入了内容,标签仍然可见(右图)

在作为参数提供的控件的“标签”的TextView上设置了labelFor属性(清单 4-11 和清单 4-12 )。提供您想要将此标签绑定到的视图的id

textView.labelFor = editText.id

Listing 4-12Setting a label for an EditText control in Kotlin

<LinearLayout
      ...
      >

      <TextView
           ...
           android:layout_marginBottom="0dp"
           android:labelFor="@+id/editText"
           android:text="@string/name" />

      <EditText
           ...
           android:id="@+id/editText"
           android:layout_marginTop="0dp"
           android:text="" />

</LinearLayout>

Listing 4-11Setting a text label for an edit text control in XML

遍历顺序

可访问性树是按照自然阅读顺序构建的。这意味着对讲和其他辅助技术将从最左上角的项目移动到最右下角的项目。这通常是正确的行为。但是有些设计,比如交错排列的元素,可能与视觉用户的阅读方式不同。这种布局的一个例子可以在谷歌 Play 商店看到,一个应用的下载量直接垂直显示在“下载”的标题下

在这种情况下,我们可能希望通过告诉 Android 当一个可访问性服务导航我们的 UI 时,哪个元素应该出现在下一个或上一个来改进我们的可访问性树。Android 有两种不同的方式来指定遍历顺序,这取决于客户使用的技术类型。如果您发现 Android 为您确定的遍历顺序不理想,那么我们需要确保我们设置了这两个选项。

可访问性遍历顺序

我们可以使用accessibilityTraversalAfteraccessibilityTraversalBefore属性来做到这一点。就我个人而言,我发现从它们的名字中理解这些属性的行为很有挑战性。设置该属性的元素是“之前”和“之后”所指的元素。让我们在图 4-8 中对此进行说明,以阐明其含义。

假设我们有三个按钮。为了清楚起见,我们将它们标为“1”、“2”和“3”。我们希望保证这些元素以正确的数字升序被遍历。为此,我们将 after 和 before 值设置为 2。2 的accessibilityTraversalBefore值将是 1,因为我们希望 1 在 2 之前被遍历。所以这使得 2 的accessibilityTraversalAfter值为 3,因为我们希望 3 在 2 之后到来。创建它的 XML 可以在清单 4-13 中看到。

<Button
      ...
      android:id="@+id/button1"
      android:text="@string/one" />

<Button
      ...
      android:id="@+id/button2"
      android:accessibilityTraversalAfter="@id/ button3"
      android:accessibilityTraversalBefore="@id/ button1"
      android:text="@string/two" />

<Button
      ...
      android:id="@+id/button3"
      android:text="@string/three" />

Listing 4-13Setting accessibility traversal order in XML

您也可以在代码中用setAccessibilityTraversalAfter()setAccessibilityTraversalBefore()方法设置这些,并传递您想在之前/之后访问的视图的id,如清单 4-14 所示。

two.setAccessibilityTraversalAfter(one.id)
two.setAccessibilityTraversalBefore(three.id)

Listing 4-14Setting accessibility traversal order in Kotlin

方向控制

在可访问性遍历顺序中,我们需要设置焦点顺序。当用户使用方向键、遥控器或键盘箭头键导航用户界面时,Android 使用焦点顺序。我们将这些设置在一个元素上,以指定当用户按下特定方向的键时,下一个应该关注哪个元素。处理箭头键输入的属性有nextFocusDownnextFocusUpnextFocusLeftnextFocusRight,处理 tab 输入的属性有nextFocusForward

对于这个例子,假设我们有一个编号为 1 到 9 的 3 乘 3 的元素网格(图 4-9 )。如果我们在中心元素,5 号呢?我们需要保证上移到 2,下移到 8,左移到 4,右移到 6,向前也移到 6。我们的中心元素(编号 5)的 XML 如清单 4-15 所示。

<Button
      android:id="@+id/button5"
      android:nextFocusUp="@id/button2"
      android:nextFocusDown="@id/button8"
      android:nextFocusLeft="@id/button4"
      android:nextFocusRight="@id/button6"
      android:nextFocusForward="@id/button6"
      android:text="@string/five" />

Listing 4-15Setting directional focus in XML

或者,如果我们需要在我们的应用代码中改变这一点(清单 4-16 ,我们也可以在这里设置这些值,传递我们希望在每个方向下一次聚焦的视图的id

button5.nextFocusUpId(button2.id)
button5.nextFocusDownId(button8.id)
button5.nextFocusLeftId(button4.id)
button5.nextFocusRightId(button6.id)
button5.nextFocusForwardId(button6.id)

Listing 4-16Setting directional focus in Kotlin

对于方向控制,我们还需要考虑另一个问题。在 Android 的这个功能的标准实现中,默认的焦点高亮不容易区分(图 4-10 ),这意味着我们必须添加自己的。

最好的方法是在 styles.xml 文件中覆盖应用主题的colorControlHighlight,如清单 4-17 所示。

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
      ...
      <item name="android:colorControlHighlight">@color/colorAccent</item>
</style>

Listing 4-17Setting our app’s theme’s control highlight color to our accent color

自定义控件

如果你坚持在应用中使用 Android 提供的控件,Android 会为你处理可访问性事件。如果您想要自定义控件,请选择与您想要实现的功能最接近的现有控件,并在其上构建。虽然有时你别无选择,只能从头开始建立控制。虽然从可访问性的角度来看不建议这样做,但是对于复杂的元素来说,这有时是必需的。如果您选择这样做,那么用不同的可访问性服务彻底测试您的控件是非常必要的。

首先,您需要确保您已经实现了本章中介绍的大多数属性。一旦你设置了这些值,Android 提供了关于你可能需要在你的控件上覆盖的其他功能的完整文档。 8 你需要实现哪些方法将取决于你的控制,但是我们将在这里讨论一些最重要的。

辅助功能操作

AccessibilityNodeInfo也是我们需要告诉 Android 辅助技术可以在我们的视图上执行的操作的地方。这是我们向客户提供更多信息的机会,让他们了解我们的控制能够做什么以及如何做。

如果你把焦点放在一个有对讲功能的按钮上,一旦该元素的值被读取,对讲功能就会显示“双击激活”。这是一个可访问性操作,由两部分组成。这个按钮告诉 Android 它能够处理一个onClick事件。从这条信息中,Android 添加了“双击以”那么“激活”就是按钮提供的一个字符串。我们需要模仿这个来控制自己。假设我们有一个新的控件,它在一个点击事件上打开一个细节视图,并在一个长按上显示一个选项菜单。我们的代码如清单 4-18 所示。

class MyView: View {

    constructor(context: Context, attrs: AttributeSet): super(context, attrs)

    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
        super.onInitializeAccessibilityNodeInfo(info)

        val click = AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, "open")

        val longClick = AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, "show options")

        info?.addAction(click)
        info?.addAction(longClick)
    }
}

Listing 4-18Adding accessibility actions to a custom view

事件处理

虽然在视图中添加一个setOnClickListener()可能会使它在客户点击屏幕时正常工作,但这并不能保证您的视图会正确响应语音访问、对讲或其他辅助服务。你应该测试你是否需要覆盖sendAccessibilityEvent()或者sendAccessibilityEventUnchecked()函数。这些方法在每次视图上触发辅助功能事件时被触发,包括聚焦和点击。

根据您的视图是如何创建的,Android 可能已经如您所料为您处理了激活事件。对于像关注具有可访问服务的元素这样的事件,您可能需要对视图进行更改,例如添加高亮显示。您可以通过检查事件是否与可访问性焦点相关来做到这一点,如清单 4-19 所示。

class MyView: View {

    constructor(context: Context, attrs: AttributeSet): super(context, attrs)

    override fun sendAccessibilityEvent(eventType: Int) {
        super.sendAccessibilityEvent(eventType)

        when (eventType) {
             AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED ->
                background = Color.BLACK.toDrawable()

             AccessibilityEvent.TYPE_VIEW_FOCUSED ->
                background =
                Color.RED.toDrawable()
        }
    }
}

Listing 4-19Setting the background color of a view red when it receives accessibility focus. Black when focus leaves the view

文本更改事件

每当控件的文本值改变时,辅助功能服务都需要知道这一点。这有助于 Android 确保呈现给辅助功能用户的内容与屏幕上显示的内容相同。为此,您的定制视图必须通过发布一个TYPE_VIEW_TEXT_CHANGED的可访问性事件(清单 4-20 )来通知 Android 值已经更改。

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)

Listing 4-20Informing accessibility about a text change on our custom view

节点信息

为了知道如何将你的视图呈现给可访问性用户,Android 收集了关于你的视图及其当前状态的信息。这包括视图的大小和位置。当 Android 请求这些信息时,它在您的视图上调用onInitializeAccessibilityNodeInfo(),传递一个初始化的AccessibilityNodeInfo供您填充(清单 4-21 )。你应该总是在这里调用 super 来让 Android 填充一些明智的默认值。然后,您可以添加控件需要的任何其他属性。这可能包括像isHeadingisCheckable这样的属性。

class MyView: View {

    constructor(context: Context, attrs: AttributeSet): super(context, attrs)

    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
        super.onInitializeAccessibilityNodeInfo(info)

             // Accessibility services will treat your view as toggleable.
        info?.isCheckable = true
    }
}

Listing 4-21Adding properties to our custom view’s accessibility info

代表

如果您选择使用提供的 Android 控件,但是希望对每个控件的可访问性如何工作有更细粒度的控制,那么您可以使用一个AccessibilityDelegate类。创建一个扩展AccessibilityDelegate的类;然后将其设置为视图上的代表(列表 4-22 )。委托可以覆盖我们之前介绍过的相同的可访问性方法。

class MyAccessibilityDelegate : View.AccessibilityDelegate() {

    override fun onInitializeAccessibilityNodeInfo(host: View,
       info: AccessibilityNodeInfo) {
        super.onInitializeAccessibilityNodeInfo(host, info)

        // add custom actions
    }
}

class MyActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.my_activity)

        val accessibilityDelegate = MyAccessibilityDelegate()

        button.accessibilityDelegate = accessibilityDelegate
       }
}

Listing 4-22Setting an accessibility delegate

语义视图

创建语义视图是一种强大的高级技术,可以创建最容易访问的应用。语义视图将两个或更多有关联意义的元素联系在一起。Android 然后将它们作为一个元素呈现给可访问性用户。这可以减少噪音,让你的应用导航更加简单、快速、轻松。如果你真的想给你的可访问性用户最好的体验,你需要使用这种技术。

谷歌在谷歌 Play 商店使用了这种技术。当查看一个应用或游戏的列表页面时,我们会在该应用的图标下看到三个框(图 4-11)——该应用的评级、下载次数和年龄评级。第一项,应用的评级,显示“4.4 ⭑”,然后在新的一行“1K 评论”此外,评级文本和年龄评级文本是按钮。如果您在启用 TalkBack 的情况下访问这些元素,而不更改可访问性树,则 TalkBack 将读取以下内容:

img/486920_1_En_4_Fig11_HTML.jpg

图 4-11

谷歌 Play 商店应用信息

img/486920_1_En_4_Fig10_HTML.jpg

图 4-10

突出日期的键盘导航——你可能看不出日期周围有方框

img/486920_1_En_4_Fig9_HTML.jpg

图 4-9

由九个按钮组成的网格。我们希望确保从数字 5 开始,方向导航就像预期的那样工作

img/486920_1_En_4_Fig8_HTML.jpg

图 4-8

三个按钮。我们希望确保它们按数字顺序导航

  • “四点四星。双击以激活。”刷卡。

  • " 100 K 加"游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游啊游。

  • “1 K 评论。”刷卡。

  • "下载。"刷卡。

  • “PEGI 3 号。双击以激活。”

刷的真多。刷一下,我们只听到“下载”这个词 100K 指的是什么?幸运的是,这不是 TalkBack 展示这一观点的方式。谷歌 Play 商店开发人员已经改进了他们的可访问性树,以提供更好的体验。相反,这是互动:

  • “一千条评论平均评分四点四星。双击以激活。”刷卡。

  • “下载了十万多次。”刷卡。

  • “内容分级 PEGI 3。双击以激活。”

将这些元素组合在一起有几个好处。首先,需要更少的刷卡次数,从五次减少到三次。意味着导航更快更简单。与分组相关的元素,如应用评论值和评论数量,提供了每个值的含义。通过将“100,000 plus”改为“100,000 plus ”,该文本也被修改为对对讲客户更有意义虽然从视觉上可以清楚地看到 K 代表千,但我们很少大声说出 K 代表千。

可聚焦容器

实现这一点的最简单的方法之一是使用可聚焦的容器。布局容器如LinearLayoutRelativeLayout是不可见的,并且不包含任何内容。但是它们包含属性,我们可以设置这些属性,使它们出现在诸如 TalkBack 之类的可访问性服务中。以谷歌 Play 商店的应用评级为例。如果我们想要实现类似的东西,我们可以使用垂直线性布局,如清单 4-23 所示。

<LinearLayout
      ...
      android:orientation="vertical">

      <TextView
           ...
           android:text="@string/rating” />

      <TextView
           ...
           android:text="@string/number_of_reviews" />
</LinearLayout>

Listing 4-23A vertical linear layout with text

目前,这个视图需要两次滑动才能进行对讲导航,可能不清楚等级值指的是什么。为了清楚起见,让可访问性服务把这个视图看作一个单独的元素。为此,我们可以将线性布局的 focusable 属性设置为 true。你可以用代码(清单 4-24 )或者直接用我们的 XML(清单 4-25 )来实现。

<LinearLayout
      ...
      android:focusable="true"
      android:orientation="vertical">

      ...

</LinearLayout>

Listing 4-25Setting a linear layout focusable in XML

linearLayout.focusable = View.FOCUSABLE

Listing 4-24Setting a linear layout focusable in Kotlin

这里的结果是每个单独的文本视图不再显示为对讲。相反,线性布局是重点,Android 已经将文本视图的值传递给线性布局。这是一个惊人的改进,但是我们仍然有一个关于读取信息的问题。我们的客户听到的不是“10 万条评论”,而是“10 万条评论”因此,让我们删除可聚焦的值,代之以将内容描述传递给线性布局。我们将在本章前面的内容描述部分介绍如何做到这一点。不要只是连接两个可视的字符串,而是生成一个新的可访问的字符串,在大声朗读时更有意义。有了内容描述,我们的线性布局已经自动隐藏了其子元素。相反,布局元素是重点,我们的新内容描述朗读。

摘要

  • Android 的可访问性服务使用一种称为可访问性树的技术来理解你的应用的 UI。这是为您构建的,但您需要调整它,以确保它对您的客户有意义。

  • 尽可能坚持使用 Android 提供的视图和控件。创建自己的控件时,可访问性很复杂。Android 已经为你做了很多艰苦的工作。

  • 将连接的视图组合成语义视图。这将使你的应用使用起来更快,更容易理解。

在这一章中,我们讨论了 Android 的可访问性系统是如何在底层工作的。您现在对如何制作一个与辅助技术共生的界面有了更清晰的想法。让我们仔细看看 Android 为我们的客户提供的一些辅助功能。我们还将讨论作为开发人员需要做出的一些决定,以便为依赖他们的用户提供最好的支持。

五、Android 辅助功能

在这一章中,我们将看看 Android 的辅助功能。这不是一个详尽的指南;我们将把重点放在可能影响你作为开发人员的特性上,因为你需要提供支持,为特性的工作做出一些决定,或者因为它可能以某种方式改变你的应用的外观或感觉。有关更多以消费者为中心的可用功能介绍,请查看谷歌的支持页面。 1

Android 的辅助功能因设备和供应商而异,平台上的许多功能也是如此。还可以通过谷歌 Play 商店添加第三方辅助工具。可访问性的一个主要方面是可定制性。对于用户来说,能够选择具有适合他们的功能的设备,然后根据需要添加和定制这些功能是一种优势。但是这确实意味着您的开发目标是一个移动的目标,所以跨设备的手动测试是必不可少的。作为本章的参考设备,我使用的是运行 Android 10 的谷歌 Pixel。

特征

设备的辅助功能设置是“系统设置”应用中的顶级选项。当你切换它的时候,Android 立即启用这个菜单中的每个选项。大多数都提供了一个简短的文本描述,说明定制做了什么以及如何做。许多网站提供了一个你正在做的改变的可视化例子,有些甚至是一个教程。这些设置都不是破坏性的,您可以通过关闭来再次禁用它们。所以我建议花几分钟时间熟悉一下这些设置的内容。尝试启用每一个;反过来,打开设置导航你的应用,看看它给你的体验带来了什么变化。你的应用还能像你期望的那样工作吗?也许你会发现,在启用这些考虑因素的情况下,你可以通过使用你的应用快速取胜。其中一些设置,如对讲,会改变您与设备的交互方式。有必要先了解一下这些特性。但如果你想陷进去,Android 会在你第一次切换 TalkBack 时给你一个全面的教程。这些辅助功能设置都是关于可定制性的,因此您可能会找到想要在自己的设备上启用的设置。

音量键快捷键

音量键快捷键是测试你的应用的绝佳工具。它允许您快速启用和禁用选定的辅助功能服务。立即在您设备的设置中启用此服务。当我们讨论不同的服务时,请在服务设置中更改此快捷方式切换的服务。这样,无论您身在何处,都可以顺畅地访问该服务,而无需在设置间来回导航。启用后,按住设备的两个音量键 3 秒钟,以切换您选择的辅助功能服务。

可用选项因设备而异,但包括设备上安装的大多数辅助功能服务。我们将在本章的后面讨论这些特性。现在,我建议切换到对讲。这样,您就有了一个快速、简单的方法来为可访问性测试启用对讲。这将有助于使屏幕阅读器设备测试成为开发流程中的常规部分。

对讲系统

当我们谈论移动设备的可访问性时,我们经常用可访问性这个词来代表屏幕阅读器导航服务,比如 TalkBack。如果你知道一个 Android 可访问性服务,它很有可能是 TalkBack。

Tip

启用该服务前,请阅读对讲导航部分。

TalkBack 是 Android 内置的屏幕阅读器服务。它使盲人和弱视用户能够听到屏幕上的内容,但它不仅仅是一个屏幕阅读器。通过宣布控件并允许用户通过定义的手势与它们交互,TalkBack 允许您的客户导航他们的整个设备,而不必看到屏幕(图 5-1 )。您的应用已经与 TalkBack 兼容,无需您进行任何设置。但是你的应用和 TalkBack 配合的如何是一个不同的问题。Android 的内置控件都是在考虑 TalkBack 的情况下创建的。因此,如果您已经使用这些控件作为基础,您可能会发现它的效果比您在做出任何改进之前可能预期的要好。

img/486920_1_En_5_Fig1_HTML.jpg

图 5-1

对讲突出显示显示设置并读取控制内容

TalkBack 要求元素具有文本表示,以便客户可以阅读该元素。对讲系统读取的信息及其顺序因元件而异。默认情况下,如果元素有文本值,TalkBack 将读取它。如果你的元素的文本很短,它可能缺少视觉用户从它周围的元素中得到的上下文。如果元素的文本很长,对于对讲用户来说可能太冗长了。在这种情况下,内容描述,一个简短的描述可访问性的字符串,可能会更好。如果存在,Android TalkBack 读取内容描述而不是文本值。TalkBack 遵循文本或内容描述,在出现时带有提示或角色;顺序取决于元素的状态和类型。我们在前一章中已经详细介绍了这些。虽然 TalkBack 只是一种可访问性服务,但它使用的可访问性树与其他服务一样。一般来说,如果你的应用和 TalkBack 配合得很好,你会发现其他辅助服务也可以。

出于测试和学习的目的,我还建议启用显示语音输出设置。你可以在开发者设置下的对讲设置中找到。这将在屏幕上显示一段当前正在朗读的祝酒辞。这将有助于您准确理解您向客户展示的内容。

Android 提供了一种简单的方法来检测可能影响应用功能的典型屏幕阅读器功能。TalkBack 允许用户与屏幕上的元素进行交互,而无需激活它们。这是通过在屏幕或特定元素上点击或执行触摸手势来完成的。这意味着这些手势不会传递到你的应用,元素的激活方式也不同。因此,了解您客户的设备是否启用了此功能可能会很有用,因为您可能需要调整您的应用如何使用触摸手势。你可以通过查询 Android 的AccessibilityManager(清单 5-1 )上的isTouchExplorationEnabled属性来实现。

val a11yManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
val isExploreByTouchEnabled = a11yManager.isTouchExplorationEnabled

Listing 5-1Determining if explore by touch is enabled

有关检测对讲或其他辅助功能服务是否启用的详细信息,请参阅本章后面的“检测辅助功能服务”一节。

更新内容

即使有一个完美的可访问性树,你可能会发现有些情况下你需要提醒对讲用户屏幕上的视觉变化。这可能是通知您的客户有新消息到达,一个长期运行的任务已经完成,或者一个游戏的分数已经改变。有两种方法可以做到这一点。但是在您决定添加任何一个功能之前,请考虑您是在为您的客户增加价值还是仅仅增加噪音。

宣布可访问性

在任何时候你都可以告诉 TalkBack 用announceForAccessibility()方法宣布你传递的任何字符串(列表 5-2 )。此方法在任何视图上都可用。

myView.announceForAccessibility("New message received")

Listing 5-2Making TalkBack announce the screen has changed

实时区域

实时区域是屏幕上更新内容与对讲用户交互分开显示的区域。一个例子可以是显示的过滤的搜索结果的数量的计数器。在搜索字段中键入时,客户会想知道他们键入的每个字符返回的结果是多了还是少了,但是在搜索字段中输入和输出每个字母会很费力。

在这些情况下,您可以将结果计数器标记为活动区域(清单 5-3 和清单 5-4 )。实时区域将在每次更新时宣布其内容,无需您的客户进行任何进一步的交互。实时区域有两种可能的模式:politeassertivepolite将在任何当前话语结束后阅读内容。assertive将中断任何现有的公告;除非绝对必要,否则避免这些。

resultsCounter.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE

Listing 5-4Creating a live region in Kotlin

<TextView
     ...
     android:accessibilityLiveRegion="polite" />

Listing 5-3Creating a live region in XML

Navigating With Talkback

TalkBack 改变了与应用交互的模式。一旦您在设备的可访问性设置中启用了此服务,TalkBack 即开始运行。因此,你必须了解它对你与你的设备交互方式的改变,尤其是当你完成时,你可以关闭这个功能!

TalkBack 以自然的方向导航你的用户界面的所有可读元素——在大多数语言中是从左上到右下——在前进的过程中用一个边框高亮显示每个元素。您可以通过点击来选择元素;一次点击将不再激活控件。向右滑动屏幕上的任意位置以导航到下一个元素,或者向左滑动以导航到上一个元素。要激活控件,您现在必须双击。

img/486920_1_En_5_Fig2_HTML.jpg

图 5-2

安卓的 TalkBack 教程应用

幸运的是,TalkBack 有一个奇妙的教程应用,允许你练习使用屏幕阅读器的功能。如果这是第一次在该设备上激活 TalkBack,只要您打开该服务,TalkBack 就会启动其教程(图 5-2 )。如果之前已经启用对讲,可以手动开始本教程。在列表底部的对讲设置中找到它。要导航到那里,请记住–点击一次以选择一个元素;双击以激活该元素。在滚动视图上向右然后向左滑动以向下翻页。在尝试使用 TalkBack 之前,请先完成教程;会让你以后少受很多挫折。

选择发言

与 TalkBack 相比,Select to Speak 是一个更“传统”的屏幕阅读器。选择朗读将按照文本内容出现的顺序朗读屏幕上出现的任何文本内容。“选择说话”不提供任何导航或与应用交互的帮助。这使得它成为那些因为视力低下、诵读困难或识字率低而难以阅读的人的理想工具。

与 TalkBack 相比,Select to Speak 在阅读内容、阅读方式和阅读时间方面有所不同。这个工具的目的是帮助处理大量的文本,例如一篇文章或电子书,或者更大篇幅的文本。选择朗读不是为了帮助导航或暗示布局的含义。因此,选择朗读会读取屏幕上显示的内容,忽略您对辅助功能树所做的任何更改。这包括忽略分组的元素和内容描述,除非具有内容描述的元素没有其他文本表示。例如,在谷歌 Play 商店上,TalkBack 会将应用的评级读作“来自 1200 万条评论的四点二星”,Select to Speak 会读作“四点二星”。十二 M 评论。”

img/486920_1_En_5_Fig3_HTML.jpg

图 5-3

选择以启用朗读,同时选择一个区域进行阅读

选择“朗读”会在屏幕的右下角添加一个辅助功能按钮。点击它,用户有两种选择:按下播放按钮以自然阅读方向阅读整个可见屏幕,或者在屏幕的一个区域上拖动手指(图 5-3 )。第二个选项绘制一个边界框;选择朗读朗读边界框区域内的任何元素。

字体大小

Android 原生支持全局缩放字体大小,以适应您客户的偏好(图 5-4 )。选项的数量因供应商而异,但谷歌的 Pixel 设备支持四种设置,从小到大。

img/486920_1_En_5_Fig4_HTML.jpg

图 5-4

Android 的最小(左)和最大(右)字体大小。外观因设备而异

你可以使用与缩放无关的像素在你的应用中实现自动字体缩放;你会在布局文件中看到这些“sp”单元(列表 5-5 )。SP 单位代表像素,但有一些可变元素。这允许设计者指定字体大小,比如 14px 然后我们可以将它作为 14sp 输入到 XML 中。在标准文本大小下,这将显示为相当于 14px。

<TextView
      ...
      android:textSize="14sp"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"  />

Listing 5-5Setting text size by scale-independent pixels

sp 单元的可变元素首先是屏幕密度。1sp 在 160 dpi(每英寸点数)屏幕上的默认文本大小相当于 1 个像素。具有更高或更低 dpi 的屏幕被适当地放大或缩小。一个固定的像素数字在不同的 dpi 中会显得更大或更小,而一个 sp 单位看起来应该大致相同。第二,sp 单位由用户选择的字体大小设置按比例缩放。这意味着确保所有文本都设置为 SP 单位将保持视图的外观,同时支持辅助功能。

为了充分支持用户不同的文本大小和屏幕密度,一个相关的考虑因素是您为显示文本的元素提供的大小。避免使用固定的高度或宽度尺寸,而是提供wrap_content。使用和组合布局,如线性布局和约束布局,将允许文本元素-编辑文本,按钮,文本视图等。–根据需要进行扩展和收缩,同时保持您的预期设计。任何包含文本的元素都应该显示在滚动视图中。这将确保用户使用任何给定的文本大小、屏幕大小、屏幕密度等组合。仍然可以阅读您的所有内容。

显示尺寸

Android 支持各种屏幕形状和尺寸的能力使其能够无摩擦地支持另一个辅助功能:可调显示屏尺寸(图 5-5 )。这对于你的应用来说是无缝的,只要你遵循一些简单的设计准则:避免固定大小的元素,更喜欢wrap_content。使用灵活的布局,如约束布局。将文本内容放在滚动视图中,并使用相对单位而不是固定单位。

例如,像素的物理大小在不同的屏幕分辨率下会有所不同,而毫米在不同的设备上具有固定的大小,但在屏幕上占据的空间大小会有所不同。相反,如果您使用 sp 单位(与比例无关的像素)为文本大小定义所有布局尺寸,使用 dp 单位(与密度无关的像素)为非文本值定义所有布局尺寸,效果会更好(清单 5-6 )。这些值根据环境变量按比例缩放。这些变量包括客户对文本大小和显示大小的设置,以及他们设备的像素密度。

<Button
      ...
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="24sp"
      android:padding="16dp" />

Listing 5-6Defining a button’s text size and padding using sp and dp units

使用 sp 和 dp 单元将在用户的各种设备上保持您的屏幕布局,并支持缩放的屏幕尺寸和缩放的文本,从而在保持高标准设计的同时帮助您的客户。

img/486920_1_En_5_Fig5_HTML.jpg

图 5-5

Android 的最小(左)和最大(右)显示屏尺寸。外观因设备而异

黑暗主题

Android 10 的新主题是黑暗主题。我知道很多开发者喜欢有黑暗主题的界面。因为我们要花几个小时看屏幕,如果没有白光照射,我们的眼睛会有很大的不同。另外,它看起来真的很酷。但是深色主题也是一个重要的辅助功能。大量明亮的光线对于患有特定视力问题(如白内障)的人来说可能是一个问题,因为光线会导致不适,并破坏人的其余视力。深色主题的高对比度也可以让色觉减退或视力模糊的人更容易区分元素。因此,虽然很容易认为深色主题很好,但对许多人来说,这是一个基本的可访问性特性。

支持黑暗主题需要对你的应用进行一些更改。首先,如果你还没有,你需要编译 API 级。支持黑暗主题的最快方法是通过将清单 5-7 中的代码行添加到 styles.xml 文件中的应用主题中来强制应用支持黑暗模式。

<style name="AppTheme"
      parent="Theme.MaterialComponents.Light.DarkActionBar">
      ...
      <item name="android:forceDarkAllowed">true</item>
</style>

Listing 5-7Forcing dark theme support for an app style

你可能会发现这适用于你的应用,但这是一个相当生硬的工具。我们正在使用我们的原始主题结合 Android 的默认黑暗主题行为。我们无法控制我们的应用如何处理黑暗主题。我们有可能需要对事物的外观进行更精细的控制。

Android 让我们能够为调色板中定义的每种颜色定义黑暗或夜晚的变化。在 Android Studio 中,在项目导航器中找到 res 目录。点击右键,选择新建➤安卓资源文件。将文件命名为颜色——我们已经有了一个同名的文件,这没关系;我们在做一个夜间变体。在可用限定符下找到夜间模式(图 5-6 ,并按下添加按钮选择它。在现在出现的夜间模式下拉框中,将值更改为“Night”,然后按 OK。

img/486920_1_En_5_Fig6_HTML.jpg

图 5-6

创建夜间模式颜色文件变体

然后你会发现你现在有两个颜色文件出现在你的项目导航器中。一个后缀为(夜)。(图 5-7)

img/486920_1_En_5_Fig7_HTML.jpg

图 5-7

Android Studio 的项目导航器中的两个颜色值 XML 文件。一个是夜间变体

appear in your project navigator. One with the suffix “

在这个新的 night variant 文件中,您可以从需要深色主题变量的原始颜色值文件中复制任何颜色值。例如,如果你的标准背景颜色是<color name="background_color">#FFFFFF</color>,你可以把它作为<color name="background_color">#FF808080</color>添加到你的夜间模式文件中。这将使你的背景在黑暗模式下变成深灰色,而不是 Android 默认的黑色。任何你不抄袭安卓的颜色都会为你调整。为了做到这一点,我们需要确保我们没有在任何布局 XML 文件或应用代码中使用任何硬编码的颜色值。总是在你的 colors.xml 文件中定义颜色,然后通过名称引用这个定义的颜色,如清单 5-8 和清单 5-9 所示。

val color = getColor(R.color.background_color)

Listing 5-9Referencing our background color value in code

<LinearLayout
      ...
      android:background="@color/background_color" >

Listing 5-8Referencing our background color value in a layout

放大

Android 有两个放大服务。放大的结果是一样的;区别在于它们是如何被触发的。顾名思义,你可以通过在屏幕上的任何地方点击三次来切换放大。使用快捷方式放大(图 5-8 )在屏幕右下角添加一个辅助功能快捷按钮,作为导航栏的一部分。轻按按钮一次,打开或关闭放大功能。

img/486920_1_En_5_Fig8_HTML.jpg

图 5-8

通过辅助功能按钮启用快捷方式放大

Android 开发人员需要注意放大有两个原因,第一个原因可能会影响任何应用。由于文本是从左到右书写的,任何放大用户通常会从屏幕的左侧向下扫描(或者在从右到左语言的情况下从右侧向下扫描)。这意味着如果你有不自然(左)对齐的内容,放大用户通常会错过这一点。例如,如果您在表格的某一行旁边添加一个表示状态的图标,如果您的图标位于文本的右侧,放大用户可能不会注意到状态图标(图 5-9 )。在可能的情况下,保持任何有意义的内容自然对齐。

img/486920_1_En_5_Fig9_HTML.jpg

图 5-9

启用放大功能导航辅助功能设置。任何右对齐的内容都可能会丢失

开发人员应该注意的另一个放大功能是使用的手势。三次点击放大将覆盖您的应用侦听的任何三次点击事件,这意味着如果启用放大,您的应用将不会接收到任何三次点击手势,即使您的用户没有激活取景器。激活后,放大功能使用两个手指滑动来移动取景器,两个手指挤压手势来放大和缩小。这意味着一旦放大功能激活,您的应用将不会收到两个手指滑动或挤压手势。解决这一问题的理想方法是,在适当启用或激活放大功能后,修改应用使用的手势。不幸的是,就目前情况来看,我还没有找到一种方法来检测这些放大服务是否处于活动状态。本章后面提到的检测放大服务的方法似乎没有返回这些服务中的任何一个。对于希望手势在你的应用中工作的放大用户来说,这可能意味着令人沮丧的体验。这只是为什么你应该为你的应用使用的任何触摸手势提供替代品的一个原因。

颜色校正

色彩校正提供了一系列滤镜,旨在帮助有色彩缺陷的用户。这些滤光器有助于增加对比度,并使颜色远离那些不足会导致区分问题的颜色。Android 通过全局覆盖来实现这一点,因此开发人员不需要做任何工作来支持颜色校正。

然而,你应该尝试应用中可用的每个滤镜,以确定每个滤镜的对比度是否仍然足够高,并看看是否仍然有可能在需要的地方从颜色使用中获得意义。你应该避免把颜色作为传达意思的唯一方式,也不要把文字和形状结合起来。一个很好的例子就是状态指示器;用绿色圆圈和红色感叹号代替红色或绿色圆圈。

颜色反转

反转颜色对几种损伤有好处。它可以减少眩光,提高对比度。这意味着光敏感、色弱或视力下降的人都可以受益于这一功能。颜色反转反转屏幕上的所有颜色。这与它们在哪里或如何使用以及应用如何设置无关。这包括图像和视频。一些用户会从倒置的图像中受益,但对许多人来说,它看起来很奇怪。如果可能的话,对你的客户来说更好的体验是支持黑暗主题。使用前面提到的深色主题,你可以选择应用反转的颜色以及反转的方式,从而在保持应用美观的同时,获得可靠的可访问体验。

移除动画

动画会引发平衡障碍患者的眩晕和恶心。对于有某些学习困难或障碍(如注意力障碍、自闭症和焦虑)的人来说,动画也可能令人担忧和分心。出于这些原因,有必要听取这个设置,并使用它来确定您是否应该减少或删除动画。启用此设置后,您应该查看应用中具有快速动画、多平面动画、缩放动画和自动播放视频的地方。

没有友好的布尔值来确定您的用户是否启用了该设置,但是如果该设置为 on,则ANIMATOR_DURATION_SCALE设置将返回浮点值 0。因此,我们可以使用清单 5-10 中的代码将它转换成我们可以在代码中使用的布尔值。

private fun areSystemAnimationsEnabled(): Boolean {
      val animationDuration = try {
           Settings.Global.getFloat(contentResolver,
      Settings.Global.ANIMATOR_DURATION_SCALE)
      }
      catch (e: Settings.SettingNotFoundException) {
           0f
      }

      return animationDuration != 0f
}

Listing 5-10A method returning false if the user has requested animations removed

切换访问

Switch access 是一项辅助功能服务,旨在通过允许您的用户使用外部硬件设备来导航和控制您的界面,从而帮助运动能力有限的人。开关可以是许多东西,例如,一个普通的键盘。也可以购买专门为此目的设计的第三方交换机。

Tip

在启用该服务之前,请阅读有关使用交换机访问进行导航的章节。

切换访问用边界框依次突出显示每个交互元素。然后,用户可以按下一个开关,跳到下一个元素或激活当前选定的元素。使用一个按钮进行导航非常耗时,这意味着交换机访问用户通常最容易受到应用超时的影响。尽可能考虑延长或取消超时。

img/486920_1_En_5_Fig10_HTML.jpg

图 5-10

切换访问突出显示一行应用

为了实现这种控制,交换机访问可访问性服务使用第四章中讨论的可访问性树。这与 TalkBack 等其他服务使用的可访问性树相同。因此,如果你的应用与 TalkBack 配合得很好,那么它与 Switch Access 配合得也很好。与 TalkBack 相比,Switch Access 只关注交互元素(图 5-10 )。因此,您的交互元素必须正确设置。使用 Android 的内置控件,如按钮和编辑文本控件,在需要的地方进行扩展,而不是实现自己的控件。例如,如果您使用一个图像并添加了一个onTouchListener,切换访问将不会访问这个触摸监听器。请改用图像按钮控件。

Navigating With Switch Access

第一次启用交换机访问时,Android 会向您呈现一个设置指南(图 5-11 )。该应用将带您完成交换机访问配置,然后为您提供一个测试区域,让您习惯交换机访问如何使与您的设备的交互变得不同。

img/486920_1_En_5_Fig11_HTML.jpg

图 5-11

交换机访问设置指南

首先,连接一个开关。虽然有专门用于开关访问的开关,但我建议使用键盘进行测试;无论是 USB 还是蓝牙都是最好的入门方式。

根据您想要使用的开关数量选择控制模式(图 5-12 )。我建议尝试这两种模式,这样你就可以了解你的 Switch Access 用户会如何体验你的应用。

img/486920_1_En_5_Fig12_HTML.jpg

图 5-12

选择可用于交换机访问的交换机数量

选择一个开关,按一下开关开始扫描屏幕。边界框高亮将在交互元素之间自动移动。按下一个元件上的开关来激活它。这种形式的互动需要很长时间,但对于行动最受限的人来说是必不可少的。

两个开关是更直接、更快速的导航模式,因为它完全按照用户的节奏来完成。一个开关导航到下一个交互元素,第二个开关激活该元素。暂时选择此选项。

接下来,选择您的扫描模式(图 5-13 )。线性扫描 2 从左上到右下依次高亮显示每一个交互元素。这种模式可能会慢一些,因为我们必须访问屏幕上的每个元素,但它可以防止一些意外的按压。行-列扫描将按行对元素进行分组。这使得浏览某些屏幕更快。按下“下一个”开关将导航到下一行元素,而不是下一个元素。如果您想要激活该行中的某个元素,请按下选择开关。然后使用“Next”开关导航到该行中您想要激活的元素。

img/486920_1_En_5_Fig13_HTML.jpg

图 5-13

在交换机访问设置中选择扫描模式

接下来的两个屏幕包括选择您的两个开关。在你的键盘上选择任意两个键,但是我建议两个相邻的键。

最后,我们会看到一个井字游戏来帮助我们练习(图 5-14 )。按下分配给你的“下一步”键开始扫描。如果你坚持使用这个设置导航,或者想尝试一些不同的东西,请按“上一步”(屏幕仍然可以使用常规触摸输入)并重新配置。在禁用开关访问之前,请记住也使用它来导航您的应用。

img/486920_1_En_5_Fig14_HTML.jpg

图 5-14

用井字游戏练习交换机访问

如果你喜欢冒险,试试开关访问设置中的其他选项。它们对你的应用界面呈现给用户的方式都有显著的影响。我建议查看以下内容:

  • 语音、声音和振动

    在此菜单中启用语音反馈。这种模式通过大声读出所有可表示文本的元素,将对讲功能与开关访问功能结合起来。这意味着切换控制将在导航时关注所有元素,而不仅仅是交互元素。

  • 自动扫描

    当开关访问配置为一个按钮时,自动扫描会以秒为间隔自动从一个元素导航到下一个元素。

  • 点扫描

    A horizontal line scans from the top of the screen to the bottom; press Select to stop (Figure 5-15). A vertical line then moves from left to right. Press Select when the crosshairs are over the element you want to activate.

    img/486920_1_En_5_Fig15_HTML.jpg

    图 5-15

    在点扫描模式下切换访问,选择驱动程序

触摸并保持延迟

触摸并保持延迟调整触发长按事件所需的时间。这有助于控制意外按压。如果您使用onLongClickListener确定长按,该设置将被考虑在内。使用这个监听器,而不是尝试自己为长点击计时。

是时候采取行动了

采取行动的时间,也称为可访问性超时,给用户额外的时间来查看临时的可视元素,并在需要时对它们采取行动。辅助功能用户浏览应用的速度通常比非辅助功能用户慢。有时候这是因为他们的能力;其他人,这是他们选择的辅助技术的功能。

该功能提供了从 10 秒到 2 分钟的各种选项。任何随后的烤面包或小吃店将显示用户选择的时间长度,覆盖您在代码中设置的值。如果您已经实现了自己的小吃店类型的控制,您不会得到这种行为。

字幕

Android 原生支持带有VideoView控件的字幕。用户可以在可访问性设置中全局启用这些选项,以及选择尺寸和颜色等选项(图 5-16 )。作为一名开发者,你可以将字幕嵌入到视频轨道中,或者通过 vtt 格式的输入流单独提供——vtt 字幕轨道通常以. srt 扩展名结尾(清单 5-11 )。一旦你这样做了,VideoView将根据你的用户的喜好来处理这些渲染。

img/486920_1_En_5_Fig16_HTML.jpg

图 5-16

Android 的字幕偏好

val subtitlesEN = getResources().openRawResource(R.raw.subs_en_vtt) // this could also be an InputStream instance loaded from a network resource.

videoView.addSubtitleSource(subtitlesEN,
      MediaFormat.createSubtitleFormat("text/vtt",
      Locale.ENGLISH.getLanguage()))

Listing 5-11Loading a subtitles file from resources and adding these subtitles to a VideoView instance

如果您在 VideoView 之外的控件中显示视频,您需要自己负责显示和呈现字幕。您可以使用清单 5-12 中的CaptioningManager类来检索客户的设置。

val captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager
val captionsEnabled = captioningManager.isEnabled
val captionStyle = captioningManager.userStyle
val textScale = captioningManager.fontScale

Listing 5-12Accessing caption settings from the system’s captioning manager

如上所示,CaptioningManager类提供了一个返回CaptionStyle类对象的userStyle值。这个类提供了诸如字体、前景色和背景色等信息。查看 Android 开发人员文档,了解完整的值范围。 3 你应该尽可能遵循这些偏好,因为你的顾客会选择这种字体、大小、颜色等的组合。是有原因的。

此外,您应该倾听客户字幕设置的变化。CaptioningManager允许我们设置一个CaptioningChangeListener(列表 5-13 ),这样我们就可以在客户更改设置时得到通知。

class MyCaptionListener:
      CaptioningManager.CaptioningChangeListener() {
      override fun onEnabledChanged(enabled: Boolean) {
                 // remove captions from your view
      }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
      val captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as

      captioningManager.addCaptioningChangeListener(MyCaptionListener())
      }
}

Listing 5-13Using a CaptioningChangeListener to detect changes in captioning settings

高对比度文本

高对比度文本使文本变成黑色或白色,这取决于文本的原始颜色。然后添加一个相反颜色的边框(图 5-17 )。这是使用 Android 的常规文本渲染系统完成的,因此在大多数情况下会为您进行处理。AccessibilityManager确实有一个值来检测此功能是否启用。但是,由于不清楚的原因,它对公共接口是隐藏的。因此,如果您选择使用isHighTextContrastEnabled方法,请小心操作。

img/486920_1_En_5_Fig17_HTML.jpg

图 5-17

标准文本(左)和高对比度文本(右)

语音接入

语音访问并不包含在 Android 的标准辅助工具套件中,而是一项辅助工具服务,可以通过搜索语音访问从谷歌 Play 商店免费下载。语音访问允许完全免提访问和控制设备。如果你在开车,这是一个有用的功能,但对于行动不便的人来说,尤其是那些行动最受限但能说话的人来说,这是无价的。语音访问仅使用语音不仅可以执行简单的任务,如谷歌助手,还可以完全控制设备。作为一个重要的辅助功能,语音访问使用起来非常有趣,所以请从 Google Play 下载并查看它。

img/486920_1_En_5_Fig18_HTML.jpg

图 5-18

启用语音访问。用户请求“打开我的应用”

像 Android 上的其他可访问性服务一样,语音访问使用我们在第四章中讨论的可访问性树。为简单起见,语音控制将在每个交互元素旁边显示数字(图 5-18 ),但这些也可以由屏幕上显示的名称触发。TalkBack 将读取元素文本值上的元素内容描述,而 Voice Access 将响应元素的文本值,但不会响应内容描述,因为内容描述是不可见的。与开关访问一样,如果您向通常不是控件的元素(例如图像)添加点击或点击监听器,语音访问将不会将其识别为可以与之交互的元素。尽可能坚持使用 Android 的标准控件;在这种情况下,请始终使用ImageButton

本地化

Android 的本地化支持是全面和完善的,以至于开发一个可以本地化的应用可能比不可以本地化的更简单。Android Lint 还会提醒你,如果你编写的代码还没有做好本地化的准备。虽然您的应用可能只在一个地区可用,但无论如何,创建一个本地化的应用都是一种很好的做法。将来当你想修改字符串或其他资源时,这会节省你很多精力。此外,本地化选项为您的业务开拓了新的市场。即使在一个市场,你会发现比你预期的更多的人更喜欢你的应用以另一种语言提供给他们。美国人口普查局发现,近 22%的美国人,大约 7000 万人,在家里说英语以外的语言。 4

当创建一个新的应用时,Android Studio 会为你创建一个 strings.xml 文件(清单 5-14 )。作为一个好习惯,总是在这里添加用户界面中使用的任何字符串。给字符串一个名称值;这是您将在代码中用来引用您的字符串,然后将您的字符串添加到<string>标记之间。虽然本节讨论的是本地化的字符串,但是您可以用同样的方式本地化任何其他资源,包括布局、绘图、维度等等。

<resources>
      <string name="app_name">My Application</string>
      <string name="action_settings">Settings</string>
</resources>

Listing 5-14A strings.xml file as generated by Android Studio

要在应用中使用这些字符串资源,请通过您在字符串值中提供的名称来引用它们。考虑我们的action_settings字符串。我们可以在使用@string/action_settings(清单 5-15 )的布局 XML 文件中引用它,或者在使用R.string.action_settings(清单 5-16 )的代码中引用它。

val string = getString(R.string.action_settings)

Listing 5-16Accessing a string resource in code

<item
      ...
      android:title="@string/action_settings" />

Listing 5-15Accessing a string resource in a layout xml file

翻译您的应用

不要试图使用自动化服务来生成翻译。这些服务不理解你的应用的上下文,经常会给你的客户带来无意义和混乱的信息。谷歌通过 Google Play 开发者控制台提供翻译服务。虽然翻译会因应用的大小和你选择的语言而异,但谷歌建议每个应用的翻译价格通常在 50 美元左右。

在我们发送应用进行翻译之前,请花些时间注释您现有的 strings.xml 文件的内容。在字符串上方添加注释,解释字符串的使用位置及其用途。包括您对该字符串的任何限制或要求。这将有助于你的翻译为每种用法选择最合适的单词或短语。

要创建字符串的本地化版本,请选择文件➤新➤值资源文件。调用文件字符串以匹配原始字符串,并在左侧列表中选择区域设置修饰符(图 5-19 )。按添加按钮。一个新的 Android 支持的区域列表出现。选择您要本地化的区域设置-您可以在此列表中键入内容进行筛选-然后按“确定”。你会看到在项目导航器中有两个 strings.xml 文件,一个是你的原始文件,另一个是你新本地化的文件(图 5-20 )。

img/486920_1_En_5_Fig20_HTML.jpg

图 5-20

我们的项目导航器中有两个字符串文件,一个作为“tlh”本地化变体

img/486920_1_En_5_Fig19_HTML.jpg

图 5-19

创建新的本地化 strings.xml 文件

在新的字符串文件中,复制任何需要翻译的 XML 字符串条目,并将<string>标记之间的值更改为新翻译的值。您选择不在此本地化的任何字符串都将恢复为原始 strings.xml 文件的值。

键盘导航

Android 支持通过键盘或遥控器进行全面导航(图 5-21 )。事实上,某些 Android 应用,如为 Android TV 开发的应用,需要这一功能。将 USB 或蓝牙键盘连接到您的手机,除了打字,您还可以使用 tab 或箭头键在元素之间导航。Android 将使用你的应用的可访问性树来决定呈现元素的顺序,但有时需要一些提示。因此,有必要连接一个键盘,看看你的屏幕导航是否如你所愿。

img/486920_1_En_5_Fig21_HTML.jpg

图 5-21

选择驱动器时的键盘导航

检测辅助功能服务

Android 的AccessibilityManager(列表 5-17 )服务将为你提供一系列关于运行你的应用的设备的可访问性环境的信息。

val a11yManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

Listing 5-17Getting the accessibility service

通过查询这个管理器,我们可以发现关于可用的可访问性服务、当前正在运行的服务以及这些服务可以为我们的客户提供什么的更多信息。为了获得所有已安装的可访问性服务的列表,我们可以请求installedAccessibilityServiceList(列表 5-18 )。

val a11yManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

val installedServices = a11yManager.installedAccessibilityServiceList

Listing 5-18Getting a list of all currently installed accessibility services

运行服务

虽然已安装服务的列表会告诉我们您客户的设备能够提供什么功能,但这并不意味着这些服务中的任何一项正在使用中。可访问性管理器提供一个布尔值,告诉我们当前是否有一个或多个服务正在运行(清单 5-19 )。

val a11yManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

val isAccessibilityEnabled = a11yManager.isEnabled

Listing 5-19Detecting

an accessibility service. Returns true if any service is running

现在我们知道了一个可访问性服务正在运行,我们可能想要更深入地挖掘正在运行的服务的类型。要了解我们是否需要做出改变,我们需要知道服务的功能是什么。我们可以通过查询可访问性管理器来做到这一点。没有检测对讲是否启用的直接请求,例如,您的客户可能正在使用其手机供应商的屏幕阅读器或谷歌 Play 商店的第三方屏幕阅读器。相反,我们基于服务的能力进行查询。Android 然后返回给我们一个列表,列出所有运行的具有该功能的服务。对于当前启用的可访问性服务的列表,我们可以使用AccessibilityManagergetEnabledAccessibilityServiceList方法(列表 5-20 )。我们需要传递一个常量来表示我们想要了解的服务类型。

val a11yManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

val screenReaders = a11yManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)

Listing 5-20Getting a list of all running screen readers

作为参数传递给该方法的FEEDBACK_SPOKEN常量返回任何能够提供语音反馈的服务,比如 TalkBack。我们可以向该方法传递不同的常数,以根据需要返回不同类型的服务。这些常量根据服务提供给用户的反馈类型或辅助功能服务所具有的功能来划分。要返回能够提供任何类型反馈的任何服务,请传递AccessibilityServiceInfo.FEEDBACK_ALL_MASK。要返回任何正在运行的服务,不管其功能或反馈类型如何,请传递AccessibilityServiceInfo.DEFAULT。我们将在这里讨论所有的常量值。

反馈

这些常量描述了服务如何向用户提供反馈。根据服务可以提供的反馈类型获取服务意味着服务当前正在运行。这并不一定意味着反馈类型目前已启用或处于活动状态。服务可以分为多种反馈类型。

  • FEEDBACK_ALL_MASK

    向您的客户提供任何类型的可访问性反馈的所有服务。

  • FEEDBACK_AUDIBLE

    提供听觉而非口语反馈的服务。

  • FEEDBACK_SPOKEN

    诸如 TalkBack 之类的口语服务。

  • FEEDBACK_BRAILLE

    盲文服务。

  • FEEDBACK_GENERIC

    不属于其他反馈类别的服务的总括属性。

  • FEEDBACK_HAPTIC

    提供触觉反馈的服务。

  • FEEDBACK_VISUAL

    视觉服务,如叠加、滤色器等。

能力

这些常量允许我们请求按能力分组的可访问性服务。根据服务的能力获取服务并不一定意味着这个能力当前是启用的或活动的。服务可以分为多个功能类别。

  • CAPABILITY_CAN_CONTROL_MAGNIFICATION

    可以控制屏幕缩放级别的辅助功能服务。

  • CAPABILITY_CAN_PERFORM_GESTURES

    一种能够在设备屏幕上模仿触摸手势的服务。

  • CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS

    任何可以请求过滤您的应用从 keys 收到的事件的服务。这包括设备硬件按键,如相机按钮,也包括连接的设备,如键盘和游戏手柄。

  • CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES

    辅助功能服务可以从设备的指纹传感器捕捉事件和手势。

  • CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION

    辅助功能服务,比如 TalkBack,允许用户通过点击来浏览 UI 元素,而不需要激活它们。如果正在运行,此功能还可以防止某些触摸手势被传递到您的应用。

  • CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT

    这些服务可以访问当前活动窗口中显示的应用内容。

  • DEFAULT

    如果服务没有提供其他功能,则为默认值。传递它将返回任何正在运行的服务。

摘要

  • Android 辅助功能因设备和供应商而异。额外的无障碍服务可以从谷歌 Play 商店下载。

  • 总是使用 Android 提供的 UI 元素,在需要的地方扩展它们。避免创建自己的控件,因为几乎不可能匹配标准控件的辅助功能。

  • 倾听客户的喜好,并尊重他们;不这样做可能会引起顾客的愤怒。

  • 尝试您的设备上可用的辅助功能;最坏的情况会是什么?你可能会找到对你有益的东西。

本章向我们概述了 Android 手机上可用的辅助功能和服务。我们还讨论了我们开发人员如何更好地支持它们,这些特性如何影响我们的应用,以及我们如何检测和响应客户的偏好。在下一章,我们将转移到 iOS,并开始研究 iOS 的可访问性系统是如何工作的。

六、iOS 可访问性模型

在我们深入研究可用的辅助功能之前,了解一下苹果的辅助功能在底层是如何工作的是很有用的。我们都熟悉我们的应用所呈现的可视化用户界面。它是你在我们设备的屏幕上看到的和与之互动的东西。我们的可视化用户界面由我们在代码中或通过界面构建器添加的控件、文本和图像组成。这些是用堆栈或约束组合在一起的,并以某种适当的颜色完成。但是你的应用有第二个用户界面,可访问性用户界面,或者可访问性树。

可访问性树

当您的 UIKit 屏幕出现时,iOS 会按照自然阅读顺序(在大多数语言中是从左上到右下)在屏幕上构建一个元素的可访问性树。这为用户选择的辅助技术创建了一个辅助用户界面(苹果称之为 AUI)。

由于 SwiftUI 的声明性,iOS 不必从视觉表示中推断我们的意图。相反,iOS 通过删除任何仅支持布局的元素,从 SwiftUI 代码构建 AUI。

AUI 上的每个可访问性元素包括关于可用元素的以下信息:标签、值、提示、特征和动作;我们将在这一章中一一介绍。AUI 不会为仅布局元素(如堆栈视图)创建可访问性树节点。但是 iOS 确实使用它们来帮助决定顺序元素应该出现在可访问性树中。

辅助功能协议和辅助功能附件修饰符

可访问性协议由 Apple 提供的所有 UIKit 元素实现。该协议负责处理关于每个元素的可访问元数据。苹果为每个视图设置了合理的默认值。这意味着您创建的任何自定义子类应该只需要调整,而不是完全实现该协议。

SwiftUI 的可访问性附件修饰符包含 UIKit 的大多数可访问性协议属性的等价物。为了避免重复,我在这里介绍了这两个方面。

可访问性元素

iOS 从视图中标记为可访问的任何元素创建可访问性用户界面,并忽略任何标记为不可访问的元素。苹果为他们提供的控件设置了一些合理的默认值。默认可访问的视图包括UILabelUITextFieldUIProgressView以及任何扩展UIControl的视图。其他视图,如UIImageViewUIView本身,默认情况下是不可访问的。

有时,使不可访问的元素对辅助技术可用可能是合适的(例如,参见第三章中关于图像替代文本的讨论),反之亦然,禁用通常可访问的视图(参见本章后面的“语义视图”一节了解更多信息)。标记为可访问的视图太多或不合适会给辅助技术用户带来噪音。太少意味着这些用户失去了上下文和功能。

为了定制哪些元素是可访问的,每个 UIKit 元素都有一个isAccessibleElement属性(清单 6-1 )。这可以根据代码要求设置为 true 或 false。界面构建器在身份检查器的可访问性标题下有一个相应的复选框(图 6-1 )。SwitftUI 元素可以设置为 accessible,或者通过在视图中添加一个布尔值修饰符.accessibility(hidden: )来设置(清单 6-2 )。

img/486920_1_En_6_Fig1_HTML.jpg

图 6-1

界面构建器中的辅助功能元素复选框

Image(systemName: "heart.fill")
    .accessibility(hidden: false)

Listing 6-2Setting a view visible to accessibility in SwiftUI

detailLabel.isAccessibleElement = false

Listing 6-1Setting a view hidden to accessibility in UIKit

Caution

如果要创建具有交互性的自定义控件或视图,请避免创建 UIView 的子类。UIControl 通常是一个更好的选择,因为这个类已经设置了合理的可访问性默认值。如果您选择子类化 UIView,请确保与可访问性用户一起对此进行彻底的测试。

标签

当辅助功能元素获得焦点时,元素的辅助功能标签是 VoiceOver 读取的第一个字符串。它应该用于快速识别该元素是什么或做什么,而不一定是该元素的内容。请将此视为您的元素名称。默认情况下,大多数视图已经有一个可访问性标签。这是你的元素的文本值。如果您的视图没有文本值,例如带有图像的按钮,或者如果您的标签很长,您可能需要添加一个辅助功能标签。

理想情况下,标签应该用一个词来表达意思,比如“玩”或“喜欢”。你的标签应该大写,不要以句号结尾。不要包含元素的类型,因为这是多余的,会增加噪声。

可以在“身份检查器”选项卡下的界面生成器中设置元素的可访问性标签。或者可以用代码中的accessibilityLabel属性来设置它(清单 6-3 )。在 SwiftUI 中,这可以使用.accessibility(label: "Send")(列表 6-4 )来设置。

Image(systemName: "heart.fill")
    .onTapGesture { ... }
    .accessibility(label: "Like")

Listing 6-4Setting an accessibility label in SwiftUI

playButton.accessibilityLabel = "Play"

Listing 6-3Setting an accessibility label in UIKit

价值

可访问性值是元素的当前值。例如,这可以是在文本字段中输入的文本、滑块的当前数值或开关的当前状态。通常,您的可访问性值将由您的控件为您定义。例如,UISlider子类总是将可访问性值设置为当前滑块值。

有时候您需要自己设置这个值。如果您正在创建一个自定义视图子类,您将需要确定您认为值的数据。如果您将子视图组合成一个具有不同值的语义视图,您将需要决定显示哪个值或者以什么顺序显示。如果多个元素有值,语义视图可能不是最佳选择。

您可以使用 UIKit 中的accessibilityValue属性进行设置(清单 6-5 )。在 SwiftUI 中。可以应用accessibility(value: )修改器(列表 6-6 )。两者都以字符串作为参数。

Image(systemName: "heart.fill")
    .accessibility(value: "100")

Listing 6-6Setting an accessibility value in SwiftUI

playButton.accessibilityValue = "100"

Listing 6-5Setting an accessibility value in UIKit

使用 VoiceOver 时,将值添加到控制与将值添加到标签末尾的意思相同。因此,我经常看到在标签上添加值。虽然这对于 VoiceOver 来说很好,但对于盲文键盘和语音控制用户来说会有负面影响。盲文键盘在单独的寄存器中显示值,表明它们是可调的。语音控制将监听元素标签。在此基础上增加价值需要您的客户提供更高的准确性。

暗示

短暂停顿后,VoiceOver 会最后朗读元素的辅助功能提示。使用提示来给出关于执行这个元素的动作的结果的进一步的上下文,但是只有当这个结果从元素的可访问性标签中不是立即明显的时候。VoiceOver 用户可以停用提示,通常只是跳过它们。因此,最好假设您的客户不会听到这些声音。使用提示作为后备来提供额外的信息,而不是对你的 UI 至关重要的东西。

在他们关于编写良好的可访问性提示的指导中, 1 苹果建议你想象向一个朋友描述控件的动作。例如,您可以说“轻按“发送”按钮发送您的信息。”如果您的可访问性特征和标签已经正确设置,那么通知您的用户元素的名称、控件的类型以及它执行的操作是多余的。所以,如果我们把这些去掉,你的提示就是“发送你的信息”避免“发送你的信息”,因为这听起来像一个指示,而不是指导。提示应该以大写字母开头,以句号结尾。

元素可访问性提示是一个字符串属性,可以在身份检查器选项卡下的界面构建器中设置(图 6-2 )。它可以在代码中用accessibilityHint属性进行配置(清单 6-7 )。在 SwiftUI 中,这可以使用.accessibility(hint: "Sends your message.")来设置(清单 6-8 )。

img/486920_1_En_6_Fig2_HTML.jpg

图 6-2

在界面构建器中设置辅助功能提示和标签

Button("Send") { ... }
    .accessibility(hint: "Sends your message.").

Listing 6-8Setting an accessibility hint in SwiftUI

sendButton.accessibilityHint = "Sends your message."

Listing 6-7Setting an accessibility hint in UIKit

特征

可访问性特征是 iOS 构建可访问性模型的基础。可访问性特征在任何 UIView 子类中都是可用的。苹果在他们提供的每一个视图和控件上设置默认值做得非常好。这意味着,在很大程度上,你不必改变苹果提供的东西。但这并不意味着不值得你花时间去检查这些默认特征是否合理。苹果并不确切知道你是如何使用你挑选的元素的。此外,特征可以随着控制状态的变化而变化。例如,如果您创建了一个秒表应用,当秒表运行时,时间标签将每秒或毫秒更新一次。这里使用特征updatesFrequently是有意义的。假设用户按下 Lap 按钮,我们冻结这个时间,并在继续计时的UITableView中添加一个新行。原来的标签现在已经把时间冻结了,不会变了。把这个标签标为updatesFrequently已经没有意义了。相反,我们应该将标识符设置为staticText

可访问性特征可以在界面构建器中设置(图 6-3 ),方法是勾选(或取消勾选)您想要更改的特征旁边的方框。在代码中,这些可以使用任何UIView上的accessibilityTraits属性来设置(清单 6-9 到 6-14 )。accessibilityTraits是一个位掩码,使它能够同时拥有多个特征。

img/486920_1_En_6_Fig3_HTML.jpg

图 6-3

在界面生成器中设置可访问性特征

.accessibility(removeTraits: .playsSound)

Listing 6-14Removing accessibility traits in SwiftUI; multiple traits can be removed with an array as above

.accessibility(addTraits: [.isHeader, . updatesFrequently])

Listing 6-13Adding multiple accessibility traits in SwiftUI

.accessibility(addTraits: .isHeader)

Listing 6-12Adding a single accessibility trait in SwiftUI

accessibilityTraits.remove(.selected)

Listing 6-11Removing a single trait in UIKit

accessibilityTraits.insert(.button)

Listing 6-10Adding a trait to existing traits in UIKit

accessibilityTraits = .none

Listing 6-9Setting a single trait removing all others in UIKit

特性在 UIKit、SwiftUI 和界面构建器中的名称和可用性略有不同(表 6-1 )。我们将依次讨论每一个特征及其影响。

表 6-1

可访问性特征名称

|

界面生成器

|

用户界面

|

斯威夫特伊

|
| --- | --- | --- |
| 纽扣 | .button | .isButton |
| 图像 | .image | .isImage |
| 静态文字 | .staticText | .isStaticText |
| 搜索字段 | .searchField | .isSearchField |
| 播放声音 | .playsSound | .playsSound |
| 键盘键 | .keyboardKey | .isKeyboardKey |
| 汇总元素 | .summaryElement | .isSummaryElement |
| 启用用户交互 | .notEnabled(IB 复选框的反码) | 没有直接特征,但是从控件的.enabled()构造函数中推断出来 |
| 频繁更新 | .updatesFrequently | .updatesFrequently |
| 开始媒体会话 | .startsMediaSession | .startsMediaSession |
| 可调节的 | .adjustable | -- |
| 允许直接互动 | .allowsDirectInteraction | .allowsDirectInteraction |
| 导致翻页 | .causesPageTurn | .causesPageTurn |
| 页眉 | .header | .isHeader |
| 环 | .link | .isLink |
| 挑选 | .selected | .isSelected |
| - | -- | .isModal |
| - | .tabBar | -- |
| (取消选择所有复选框) | .none | -- |

没有人

这个元素没有特别的可访问性特征。

纽扣

这个元素是一个交互式按钮。这种特性会导致 VoiceOver 在阅读项目文本后发出“按钮”的声音。它还使元素对语音控制和开关控制可见。

导航到不同屏幕的内嵌链接,如网页中的链接。这种特性会导致 VoiceOver 在阅读项目文本后宣布“链接”。这个特性告诉语音控制和开关控制这个元素是交互的。

搜索字段

允许您的客户输入要搜索的字符串的文本字段。这一特征将该字段与标准文本字段区分开来。它提示用户,在这里输入文本会导致 UI 在其他地方更新。

图像

任何没有文本和动作的图像或视觉元素,也就是说,你不应该把这个特性应用到图像按钮上。请参见第三章中关于图像替代文本的讨论,了解何时使图像可访问。

挑选

当前选定的项,如选项卡或分段控件上的项。

播放声音

一旦被激活就会触发声音的元素。这将告诉 VoiceOver 在激活此元素时停止任何发声。

键盘键

用作键盘上的键的项,例如,如果您正在实现自定义输入控件。这允许与按键直接交互,以及 VoiceOver 焦点。

静态文字

在视图的整个生命周期中不变的文本。

汇总元素

摘要元素特征描述了在屏幕上提供信息概览的区域。这方面最好的例子就是苹果内置的天气 app(图 6-4 )。打开一个位置时,VoiceOver 会聚焦在标记为摘要元素的顶部区域。然后,VoiceOver 会朗读所选位置的当前天气状况摘要。

img/486920_1_En_6_Fig4_HTML.jpg

图 6-4

苹果的天气应用,带有突出顶部摘要元素的 VoiceOver

未启用

此项目已被禁用,如果被激活,将不会发生任何事情。请注意,Interface Builder 以相反的方式显示此复选框,启用用户交互。在 SwiftUI 中,这种特性没有直接的对等物。SwiftUI 从视图的.disabled()构造函数中确定这个属性。

经常更新

这种特性适用于更新标签或值过于频繁而无法发布UIAccessibilityLayoutChangedNotification通知的元素。这告诉用户选择的辅助技术以适当的时间间隔轮询该元素的值和标签变化。这方面的一个用例是时间显示。

开始媒体会话

用于激活后开始播放或录制媒体的元素。一旦元素被激活,这种特性会导致 VoiceOver 语音暂停,从而防止媒体会话被中断。

可调节的

将这一特性用于滑块或选择器等元素,用户可以从一系列值中进行选择。确保你的控件也实现了accessibilityIncrement()accessibilityDecrement()。我们将在本章的后面讨论这些。这个特质在 SwiftUI 中没有对应的特质。

允许直接互动

允许直接交互告诉 VoiceOver 此视图不应偏离标准 UIKit 触摸控制。

假设你创建了一个音乐 app,提供钢琴键盘给用户弹奏(图 6-5 )。使用划动按键和双击的 VoiceOver 范例不会产生太多的曲调。允许直接交互仅对此控制停用 VoiceOver 手势导航。这允许您的用户通过轻按按键来弹奏键盘,而无需为 UI 的其余部分停用 VoiceOver。不恰当地使用这一特性会给 VoiceOver 用户带来更糟糕的体验。

img/486920_1_En_6_Fig5_HTML.jpg

图 6-5

Apple 的 Garage Band 应用,带有突出显示键盘视图的 VoiceOver。这种观点具有允许直接互动的特点。允许在键盘上进行多点触控交互,同时保持键盘上方控件的 VoiceOver 导航

导致翻页

这个特征向屏幕读者表明,这个内容代表一组页面中的一页,比如电子书。

这个特性导致屏幕阅读器在阅读完内容后立即调用这个视图上的accessibilityScroll()方法。然后,屏幕阅读器将开始读取任何新内容。如果调用此函数后内容没有变化,读取将停止。

页眉

导航栏的标题或任何分隔内容的大文本标题元素。

通过垂直滑动或调整转子控制,VoiceOver 用户可以利用这一特性浏览您的内容。这有助于确定哪些内容与他们的需求相关,而不必扫描整个屏幕。

选项卡栏

这一特征表明视图不是直接交互的,而是包含可以交互的选项卡按钮。任何具有该特征的元素都必须为isAccessibilityElement返回 false。

情态的

这个特性只有 SwiftUI 才有。它是 UIKit 的accessibilityViewIsModal属性的 SwiftUI 版本。这一特性导致辅助技术忽略屏幕上任何其他视图的内容,只允许该视图的子视图访问。

语言

元素的辅助功能语言属性允许我们指定希望 VoiceOver 为该元素使用的语言规则(清单 6-15 )。它是一个遵循 BCP 47 规范 2 的字符串值,告诉 VoiceOver 应该以用户系统语言之外的语言读取该元素的可访问性标签、值和提示。如果不需要替代语言,则不需要设置该值。

// es-419 is the code for Latin American Spanish.
spanishGreeting.accessibilityLanguage = "es-419".

Listing 6-15Setting an element’s accessibility language

隐藏的元素

隐藏的元素,accessibilityElementsHidden(列表 6-16 ),是一个只有 UIKit 的布尔值。它告诉辅助技术任何子视图都是不可访问的。当一个视图被另一个动画视图部分遮挡时,可以使用此选项。

contentView.accessibilityElementsHidden = true

Listing 6-16Setting children of contentView hidden to accessibility

视图是模态的

accessibilityViewIsModal(清单 6-17 )是一个只有 UIKit 才有的布尔属性。它会导致辅助技术忽略屏幕上除此视图之外的任何视图。辅助技术只能访问设置了它的视图的子视图。在 SwiftUI 中,您可以通过应用.isModal特征来做同样的事情。

customAlert.accessibilityViewIsModal = true

Listing 6-17Setting the customAlert view modal for assistive technologies in UIKit

元素顺序

辅助技术从左上到右下呈现屏幕。大多数时候,这对我们的界面是正确的。但有时这使得阅读屏幕不合逻辑。如果你的界面中有交错排列的元素,或者你是垂直显示元素,就会发生这种情况。

以 App Store 为例。在应用或游戏的列表页面上,应用图标的正下方是一个栏,显示了该应用的一些详细信息:下面是评级数量的星级评定,下面是应用类别的排名,下面是标题为“年龄”的年龄评定。如果 VoiceOver 在没有对我们的 AUI 进行任何修改的情况下阅读这篇文章,结果将是“4 星”。第一。12+.14K 收视率。策略。年龄。”这些信息杂乱无章,毫无意义。

相反,VoiceOver 读“4 颗星”会更有意义。14K 收视率。”为了做到这一点,我们的子视图可以告诉可访问性,它的元素是否应该以特定的顺序访问。我们创建的每个子视图都向 UIAccessibility 返回一个accessibilityElements数组。iOS 通常会为我们生成这个,但是我们可以有更多的控制权。在这个例子中(清单 6-18 ,我们使用一个UIView子类作为三个 header 和三个 detail 元素的容器。我们对每个元素都有一个@IBOutlet引用;然后我们告诉UIAccessibility我们希望这些元素被遍历的顺序。我们没有添加到这个数组中的任何元素都将被辅助技术忽略。

class DetailView: UIView {

    @IBOutlet weak var header1: UILabel!
    @IBOutlet weak var header2: UILabel!
    @IBOutlet weak var header3: UILabel!
    @IBOutlet weak var detail1: UILabel!
    @IBOutlet weak var detail2: UILabel!
    @IBOutlet weak var detail3: UILabel!

    override var accessibilityElements: [Any]? {
        set{}
        get{
            return [header1!,
                    detail1!,
                    header2!,
                    detail2!,
                    header3!,
                    detail3!]
        }

    }
}

Listing 6-18Informing UIAccessibility of the order we’d like our elements traversed in a subview

逃跑

有辅助技术的导航通常比没有辅助技术的慢,原因有几个。有时,当把屏幕作为一个整体来看时,可能会更清晰的上下文会丢失。为此,苹果提供了一些全局快捷方式。其中之一是神奇的水龙头;我们将在本书后面的画外音部分讨论这一点。另一个支持 VoiceOver 和开关控制的快捷键是 Escape(清单 6-19 )。

img/486920_1_En_6_Fig6_HTML.jpg

图 6-6

开关控制的退出命令

通过开关控制,当选择任何元素时,可以从动作菜单中选择退出选项(图 6-6 )。使用 VoiceOver 时,可以用两个手指在屏幕上画一个 Z 形来进行转义。如果你使用的是标准的UINavigationController,,你可以免费获得这个行为。您可能会发现,如果您要呈现定制的模态元素,就需要使用它。

override func accessibilityPerformEscape() -> Bool {
      // Dismiss the current view.

      return true // return false if the view can’t be dismissed.
}

Listing 6-19Supporting the accessibility Escape command

自定义操作

加速辅助功能导航并为辅助功能用户创造更好体验的一个好方法是使用自定义辅助功能操作。自定义动作允许我们向只能通过辅助技术访问的元素添加动作。在开关控制中,当控制被聚焦时,这些动作显示在控制选项中(图 6-7 )。使用 VoiceOver,当元素被选择时,控制将显示“可用操作”。然后可以通过垂直滑动来循环这些动作。

img/486920_1_En_6_Fig7_HTML.jpg

图 6-7

“10 个旅行者”的自定义可访问性操作

为了确定任何可用的自定义动作,可访问性 API 将查询视图的accessibilityCustomActions属性(清单 6-20 )。这将返回任何可用操作的数组。通过初始化一个UIAccessibilityCustomAction对象来创建新的动作。如果成功,您的操作应该返回 true。

override var accessibilityCustomActions: [UIAccessibilityCustomAction]? {
    set {}
    get {
        return [UIAccessibilityCustomAction(name: "10 travelers") { customAction in
            self.slider.value = 10
                return true
            }]
        }
    }

Listing 6-20Returning a new custom accessibility action for a view subclass

控制焦点

在某些情况下,通过辅助技术了解元素何时获得或失去焦点是很有用的,比如说,如果您需要更改视图的状态或想要添加辅助突出显示。可访问性 API 为您提供了两个函数来接收这些事件的更新。accessibilityElementDidBecomeFocused()accessibilityElementDidLoseFocus()(列表 6-21 )。这些事件由 VoiceOver 和开关控制触发。

override func accessibilityElementDidBecomeFocused() {
    // this element became focused, change status as needed.
}

override func accessibilityElementDidLoseFocus() {
    // this element lost focused, reset the elements status.
}

Listing 6-21Listening for changes in accessibility focus for an element

框架和激活点

accessibilityFrame(列表 6-22 )标记了当聚焦于一个元素时,开关控制等辅助技术将用来绘制高亮的界限。这通常是视图的框架,但是如果您确实需要扩展框架,这个属性允许您这样做。

myView.accessibilityFrame = CGRect(x: myView.frame.origin.x - 10,
    y: myView.frame.origin.y – 10,
    width: myView.frame.size.width + 20,
    height: myView.frame.size.height + 20)

Listing 6-22Setting a custom accessibility frame

如果你调整你的元素的框架,你也应该设置激活点。这是屏幕上的点,辅助技术将在激活控件时合成一个点击。您可以通过向accessibilityActivationPoint传递一个CGPoint来实现这一点。

增量和减量

一些辅助技术改变了我们与屏幕上元素的交互方式。画外音和开关控制就是两个例子;我们将在后面的章节中更多地讨论他们如何以及为什么这样做。但这意味着我们需要一种新的控制模式,而不仅仅是简单的点击激活。滑块就是一个很好的例子。UIKitUISlider控制实现了两个功能,accessibilityIncrement()accessibilityDecrement()

img/486920_1_En_6_Fig8_HTML.jpg

图 6-8

开关控制的增量和减量控制

任何使用.adjustable可访问性特征的视图都必须覆盖这两个函数。当用户的辅助技术执行递增或递减操作时,将调用这些函数。这可以是点击开关控制上的增量开关(图 6-8 )或者用 VoiceOver 垂直滑动。然而,iOS 将UISlider控件呈现给可访问性的方式存在问题,所以让我们来看看如何解决这个问题。无论何时使用UISlider控件,你都应该在自己的应用中做类似的事情。如果你选择这样做的话,这也会给你知识来创建你自己的可访问的可调节控件。一般来说,我会一直推荐使用UIKit提供的控件。

Accessible Slider

克隆这本书的 git repo,在第六章下,寻找滑块练习。这里我们将创建一个可访问的滑块控件。

这个例子是一个应用的一部分,它允许客户预订去我们太阳系不同地方的假期。这个例子中包含的屏幕是火星列表的细节。在屏幕中央,我们有一个滑块控件,让我们的客户输入他们为多少旅行者预订。当您增加或减少滑块时,我们会更新上面的标签以显示所选的数字。

如果您使用 VoiceOver 来尝试此控制,以下是读取的内容。"旅行者:0。"然后我们滑动到滑块。"百分之零,可调。"如果我们用 VoiceOver 向上滑动来执行辅助功能增量操作,答案是“10%”

这里有几个我们可以解决的可访问性问题。虽然太空是一个危险的地方,但我不认为我们的任何客户会愿意带着一定比例的旅行者,相反,他们更喜欢整个人。所以我们应该改变画外音的回应。此外,盲人用户如何知道这个滑块的用途?之前有一个“旅行者”的标签,但是我们怎么知道这个滑块控制这个值呢?

我们可以通过将这两个元素组合在一起来解决这两个问题。在本例中,我已经将这两个元素放在 UIView 子类中。当滑块改变时,子类处理标签更新。在您自己的项目中,将滑块和标签添加到视图中,如果您还没有这样做的话。

首先,我们需要告诉 accessibility API 一些关于容器视图的信息。它需要知道视图是可访问的并且是可调整的。让我们在 init 中这样做。

required init?(coder: NSCoder) {
    super.init(coder: coder)

    isAccessibilityElement = true
    accessibilityTraits.update(with: .adjustable)
}

现在,如果你在这个屏幕上运行 VoiceOver,你的容器视图将被聚焦,你会被告知它是可调的。但是没有标签,如果我们试图调整它,什么也不会发生。所以现在我们只是让体验变得更糟。让我们添加一个可访问性标签和值。

override var accessibilityLabel: String? {
    set{}
    get{
        return "Travelers"
    }
}

override var accessibilityValue: String? {
    set{}
    get{
        return "\(Int(slider.value))"
    }
}

现在辅助功能用户可以知道控件是做什么用的,但是他们仍然没有办法控制它。这就是我们使用accessibilityIncrement()accessibilityDecrement()函数的地方。在这个例子中,我们只想改变我们的滑块,我们已经写的代码将处理其余的。所以我们需要做的就是将这些交互传递给滑块。如果您正在创建自己的自定义控件,您需要在这里添加适当的逻辑来处理每个交互。

override func accessibilityDecrement() {
    slider.accessibilityDecrement()
}

override func accessibilityIncrement() {
    slider.accessibilityIncrement()
}

我们还需要更新一个行为。使用原来的滑块时,每次我们更改滑块时,VoiceOver 都会告诉我们滑块的百分比值。这对我们没什么帮助,因为滑块控件的值是浮点数,所以 VoiceOver 总是给我们读一个百分比值。但那种行为比我们现在听到的要好得多,那根本不算什么。因此,让我们模仿这种行为,但使它更适合我们的使用。我们已经在sliderValueChanged()函数中更新了代码中的可视标签。这似乎也是更新 VoiceOver 客户的最佳机会。找到这个函数,我们在现有代码后添加一行。

@IBAction
func sliderValueChanged() {
    ...

    UIAccessibility.post(notification: .announcement,
        argument: "\(rounded)")
}

现在,当我们在这个控件上运行 VoiceOver 时,我们减少了滑动次数,增加了清晰度。以这种方式一起使用可访问性工具来创建复合可访问性视图是一种称为语义视图的技术。这是一个强大的工具,如果你想让最易访问的界面成为可能,这是必不可少的。

语义视图

如果你想让你的应用的可访问性更上一层楼,创建语义视图是必须的。语义视图是由多个元素组成的视图,这些元素组合在一起是为了便于访问,因为它们一起具有意义或语义。这项技术是关于把我们在本章中提到的所有可访问的用户界面工具整合在一起。目标是为易访问性客户创建一个更容易理解和更快导航的界面。

当你使用辅助技术导航时,你会注意到 iOS 中经常使用的语义视图。从 iOS 的“文件”应用中查看图 6-9 中的表格单元格。这里有几条信息:文件名、日期、大小和下载按钮。如果这些是单独呈现的,那就要用画外音扫四下。如果我们激活了下载按钮,将不会清楚哪个文件会被下载。相反,您可以看到 VoiceOver 将单元格高亮显示为一个元素。

img/486920_1_En_6_Fig9_HTML.jpg

图 6-9

VoiceOver 将单元格及其内容高亮显示为一个语义视图

有时候 iOS 可以聪明到在一些UITableViewCell设计上为你创建语义视图。大多数情况下,我们需要手动创建它们。有几种方法可以构建语义视图。

自定义视图

我们在前面的辅助滑块练习中介绍了这种技术。在那个练习中,我们使用了一个UIView作为容器。我们为视图创建了一个定制的UIView子类。然后,这个视图处理可访问性交互,并代表我们的控件将信息反馈给可访问性 API。这是我的首选方法,因为这个组件可以在你的应用中重用。iOS 也给了你很多免费使用标准的UIAccessibilityUIView功能。

基本框架

创建语义视图的另一种方式是调整视图的accessibilityFrame来包含其他元素。然后,通过将isAccessibleElement设置为 false,您可以从可访问性树中移除重叠的元素。根据需要将任何其他元素的可访问性属性传递给带有扩展框架的视图。

这项技术要求您仔细计算框架,并在布局改变时重新计算它们。例如,假设您的客户更改了字体大小或旋转了屏幕。使用这种方法最适合简单的视图,比如没有任何交互的视图。如果你正在展开的视图是一个控件,你需要设置accessibilityActivationPoint来确保辅助技术激活你的控件,而不是你展开的框架的中心。

Semantic View Frame

img/486920_1_En_6_Fig10_HTML.jpg

图 6-10

用 VoiceOver 分别访问这两个标签是不必要的冗长

使用这种技术的一个例子是垂直堆叠的元素。如果我们想显示一个星级和下面的评论数量,将这些作为一个元素分组是有意义的。首先,让我们在我们的viewDidLoad回调中添加几行代码来设置标签并隐藏不需要的视图。

class ViewController: UIViewController {

@IBOutlet weak var rating: UIButton!
@IBOutlet weak var responses: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()

    responses.isAccessibilityElement = false
    rating.accessibilityLabel = "\(stars) stars from \(respondents) ratings"
    }
}

第二步是计算框架。我们需要在viewDidLayoutSubviews中这样做,以确保如果视图改变,iOS 重新计算可访问性框架。

img/486920_1_En_6_Fig11_HTML.jpg

图 6-11

我们新的语义视图只需一次滑动,更有意义

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let ratingX = Float(rating.frame.offsetBy(dx: 0,
dy: 0).origin.x)
    let responsesX = Float(responses.frame.offsetBy(dx: 0,
dy: 0).origin.x)

    // Find the left-most item.
    let x = CGFloat(fmin(ratingX,
                             responsesX))
    let y = rating.frame.origin.y

    let ratingWidth = Float(rating.frame.offsetBy(dx: 0,
dy: 0).size.width)
    let responsesWidth = Float(responses.frame.offsetBy(dx: 0,
dy: 0).size.width)

    // Match the width of the widest element.
    let width = CGFloat(fmax(ratingWidth,
                                 responsesWidth))

    // Calculate the height of both elements plus the padding between each.
    let height = rating.frame.size.height + padding + responses.frame.size.height

    rating.accessibilityFrame = CGRect(x: x,
                                       y: y,
                                       width: width,
                                       height: height)
}

如果现在充当语义视图的元素是一个按钮,那么还需要一个步骤。记得告诉辅助技术人员accessibilityActivationPoint在按钮元素上,而不是在框架的中心。在viewDidLayoutSubviews中这样做,这样每次你的屏幕改变时都会被计算。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    ...

    rating.accessibilityActivationPoint = CGPoint(x: rating.frame.midX,
        y: rating.frame.midY)
}

SwiftUI 斯塔克斯

SwiftUI 中引人注目的可访问性变化之一是使用单个修饰符创建语义视图的机制。在 SwiftUI 中,你的大部分布局都是使用堆栈来完成的,可以是HStackVStackZStack。这些堆栈中的每一个都可以包含许多不同种类的视图。标准行为是堆栈按照视图在堆栈中出现的顺序将所有这些视图呈现给可访问性。使用.accessibilityElement(children: )修改器,我们可以改变这种行为。

如果我们通过了。combine对于accessibilityElement修饰语,辅助技术并不单独关注每一个元素。相反,它们关注于堆栈,并且堆栈的每个子级的可访问性属性都被传递给堆栈。

通常情况下,使用.ignore值可以给客户带来更好的体验。可以预见,Ignore 会忽略堆栈中的所有视图。Ignore 也使堆栈成为可聚焦的。但是堆栈本身没有可访问性属性,所以我们需要添加任何标签、特征、提示和动作。如果我们想创建一个类似于前面评级视图的视图,我们的代码应该如清单 6-23 所示。

VStack (alignment: .leading, spacing: 10) {

    Button("⭐⭐⭐⭐") { self.tappedRatings() }
        .font(.largeTitle)

    Text("1K ratings")
        .font(.subheadline)
}
.padding()
.accessibilityElement(children: .ignore)
.accessibility(label: Text("\(stars) stars from \(respondents) ratings"))
.accessibility(addTraits: .isButton)
.accessibilityAction { self.tappedRatings() }

Listing 6-23SwiftUI semantic view

Twitter 示例

Twitter 提供了一个很好的案例研究,说明语义视图在哪里产生了巨大的影响。尝试在启用开关控制和 VoiceOver 的情况下导航 iOS Twitter 应用,并记录阅读推文时的体验。如果你有其他的 Twitter 客户端,试一试,看看它们的不同之处。

当你在视觉上浏览时间线时,你不会查看头像、Twitter 名称、句柄、推文、链接、喜欢、转发等等。你在看推特。但是每条推特都是由一大堆其他东西组成的。这里有一条来自 Accessibility London meetup 的推文(图 6-12);VoiceOver 正在高亮显示该推文。如果 VoiceOver 关注每一个元素,这将使浏览 Twitter 变得非常令人沮丧。让我们看看如何创建类似的东西。

img/486920_1_En_6_Fig12_HTML.jpg

图 6-12

由画外音聚焦的推文

Semantic Cell

从本书的 GitHub repo 中,打开语义单元示例。这里我们有一个用于社交媒体帖子的自定义表格单元格。我们有用户的头像、姓名和帖子内容。下面是评论、喜欢和分享的数量。每个按钮都可以让我们评论、喜欢或分享。头像也是用户资料的一个按钮。

启动 VoiceOver 并查看为该单元格读取的内容。iOS 已经为我们做了很多工作,将所有单元格的内容组合在一起。滑动到下一个元素,你会看到这篇文章上的一个按钮,而不是时间线中的下一篇文章。这将使得浏览数百个帖子非常耗时。

首先,让我们从可访问性标签中删除用户名。这对 VoiceOver 用户来说并不重要,而且会增加噪音。我们通过设置自己的可访问性标签来做到这一点。

class SemanticCell: UITableViewCell {

    ...

    var model: TimelineEntry! {
        didSet{
            ...

            accessibilityLabel = "\(model.user.name). \(model.content)"
        }
    }
}

接下来,我们应该从可访问性界面中移除按钮。这将意味着帖子之间的导航更加顺畅。

class SemanticCell: UITableViewCell {

    ...

    var model: TimelineEntry! {

        didSet{
            ...

                comment.isAccessibilityElement = false
                share.isAccessibilityElement = false
                like.isAccessibilityElement = false
                avatar.isAccessibilityElement = false

        }
    }
}

很好,除了现在我们的可访问性用户不能访问这些功能。让我们将它们作为辅助功能操作添加回去。出于本练习的目的,我们实际上不会在动作中添加任何代码。在您自己的应用中,您会希望在这里添加一些代码。

class SemanticCell: UITableViewCell {

    ...

    var model: TimelineEntry! {
        didSet{
            ...

                let likeAction = UIAccessibilityCustomAction(name: "Like, \(model.likes)") {_ in
                return true
            }
            let shareAction = UIAccessibilityCustomAction(name: "Share, \(model.shares)") {_ in
                return true
            }
            let commentAction = UIAccessibilityCustomAction(name: "Comment, \(model.comments)") {_ in
                return true

            }
            let profileAction = UIAccessibilityCustomAction(name: "view \(model.user.name) profile") {_ in
                return true
            }
            accessibilityCustomActions = [commentAction, likeAction, shareAction, profileAction]
        }
    }
}

摘要

  • 控制辅助技术如何与您的应用交互的系统由所有 iOS 辅助技术共享。

  • 苹果称之为无障碍用户界面;您可能熟悉同一个概念,称为可访问性树。

  • iOS 为我们做了很多开箱即用的事情;坚持使用标准的 iOS 视图控件,你的应用的可访问性会很好。测试你的应用,最好是有真正的可访问性用户,找出你需要调整的地方。

  • 语义视图是将你的应用的可访问性提升到一个新水平的重要技术。

既然你已经知道了 iOS 可访问性是如何工作的;让我们来看看 iOS 为您的客户提供的一些辅助工具。我们将了解您如何最好地支持他们,以及如果您发现您的客户启用了其中一项功能,您可能需要考虑哪些事项。

七、iOS 辅助功能——概述

苹果的人机界面指南 1 (通常被称为 HIG)是任何为 iOS 开发移动应用的人的必读之作——不仅仅是设计师。HIG 阐述了苹果如何让 UIKit 对你这个应用开发者变得灵活,同时对你的用户变得清晰而有意义。

在考虑如何为你的 iOS 应用引入可访问性时,关于可访问性的章节 2 是一个起点。HIG 为您提供了使用 iOS 内置可访问性注意事项的最佳概览。让你的应用与本指南保持一致(不仅仅是可访问性部分)将有助于你的用户在你的应用中有宾至如归的感觉,因为苹果设计的许多系统模式将移植到你的应用和其他应用中。此外,遵循本指南并确保高水平的可访问性意味着您的应用更有可能出现在 App Store 上。UIKit 和 SwiftUI 都提供了强大的定制选项,允许您在保持可访问性的同时,为您的应用提供独特的外观和感觉。

人们使用 苹果的辅助功能 ,例如降低透明度、画外音和增加文本大小,以适合他们的方式个性化他们与设备的交互方式。可访问的应用通过设计支持这种个性化,并为每个人提供出色的用户体验,无论他们的能力或他们如何使用他们的设备。

—苹果人机界面指南:可访问性

我向苹果的可访问性团队询问了他们从客户那里收到的关于第三方应用可访问性的反馈。他们告诉我:

我们收到的关于第三方应用可访问性的两个最常见的反馈涉及元素标签和 VoiceOver 的导航顺序。此外,调色板选择、黑暗模式支持和媒体字幕都是很好的实践。

—苹果无障碍团队 3

我们在第六章的“可访问性协议”一节中介绍了可访问性标签。在这一章的后面,我们还包括了改进导航顺序的技术,比如语义视图。有关选择颜色的可访问性规则,请参见第三章中的可区分指南。有关包含字幕的指南,请参见第十章。

在这一章中,我们将介绍 iOS 系统范围的辅助功能,为什么有人会启用它们,以及启用它们会如何影响你的应用。这不是辅助功能设置的完整列表;从最终用户的角度来看,更多关于可用内容的详细信息,请参阅 Apple 网站的可访问性部分。相反,本章关注的是可能会改变你的应用外观或工作方式的可访问性设置,或者可能需要你添加代码或做出决定来最好地支持它们的设置。

如果您使用推荐的系统 APIs 动态类型、可访问性特征等。,您将免费获得其中的许多功能。SwiftUI 的可访问性支持远远好于 UIKit,原因在前一章中已经提到,但是仍然需要一些定制。如果你通过 web 视图或其他跨平台系统创建一个非本地应用,辅助工具通常会模仿你正在编译的系统。最终,就像任何跨平台的东西一样,特性会有所不同,而且可能会受到限制。虽然苹果会尽可能地为你做,但有些设置需要你查询 iOS 的UIAccessibility框架进行设置,并自行决定如何处理。

从 iOS 13 开始,辅助功能菜单现在是系统设置中的顶级菜单(图 7-1 ),分为四个标题,涵盖了该技术旨在帮助的障碍类别——一般、视觉、听觉以及身体和运动。在 iOS 12 和更早的版本中,这些功能可以在设备的“系统设置”中的“通用➤辅助功能”下找到。

img/486920_1_En_7_Fig1_HTML.jpg

图 7-1

iOS 13 中的辅助功能设置(右)位于设备设置的顶层(左)

该菜单中的每个辅助功能选项都提供了启用该选项的简短描述。每个选项一旦被触发,就会立即启用并呈现在整个系统中。启用后,引导式访问需要一个额外的步骤来激活该功能。

花些时间浏览一下这份菜单;启用每个选项,浏览您的应用,查看每个选项如何改变您的应用的外观和行为。没有任何设置是破坏性的,可以立即禁用。但是,某些设置(如 VoiceOver)会改变设备的功能。因此,首先阅读一些关于这些特性的内容是值得的,至少这样你就知道如何在完成后禁用它们。该菜单完全是关于可定制性的,因此您可以找到您想要在您的个人设备上保持启用的选项。

一般特征

苹果将其无障碍考虑分为四类——认知、运动、视觉和听觉(图 7-2 )。出于这个原因,我将可访问性设置分成了四个相似的类别。运动、视觉和听觉反映了苹果的类别。虽然没有具体的设置来帮助那些有认知障碍的人,但许多其他设置也会帮助那些属于这一类别的人。在第一部分,我们将介绍 iOS 的一些常规设置和功能。

img/486920_1_En_7_Fig2_HTML.jpg

图 7-2

苹果在 2018 年 WWDC 车展上展示了其四类无障碍考虑因素

辅助功能快捷方式

我首先提到这一点,因为这将允许您快速简便地在 iOS 上启用许多辅助功能。因此,这是测试您的应用与许多 iOS 辅助工具协同工作的最佳方式。启用这个简单的特性将鼓励您将这些特性作为开发和测试工作流的一个常规部分来激活。重要的是,这也是禁用这些功能的最简单方法,一旦您启用了改变设备工作方式的功能,这可以省去很多麻烦。

Tip

在尝试本章中的任何内容之前,请启用辅助功能快捷方式。

进入设置➤辅助功能➤辅助功能快捷方式,在设备的辅助功能设置中启用辅助功能快捷方式。您会发现辅助功能快捷方式是列表中的最后一项。我强烈建议启用列表中的每一项,这样你就可以在需要的时候快速地尝试一下。

img/486920_1_En_7_Fig3_HTML.jpg

图 7-3

启用此列表中的每个可用选项,以便以后轻松访问该功能

该菜单上可用的选项有辅助触摸、经典反色、智能反色、滤色器、放大镜、减少白点、开关控制、画外音、缩放、引导访问和语音控制(图 7-3 )。但是,具体选项可能会因设备的设置而异。我建议在您的测试设备上启用这些功能;我们将在本章中更详细地介绍这些特性。

img/486920_1_En_7_Fig4_HTML.jpg

图 7-4

按下主屏幕或睡眠按钮三次后激活的辅助功能快捷方式

然后,您可以在需要时激活辅助功能快捷方式,方法是在旧设备上连按三次侧边按钮或主屏幕按钮。这将显示一个模式菜单(图 7-4 ),您可以在其中激活或取消激活您从前面列表中选择的功能。我强烈建议启用这个快捷方式,因为它不会影响你对 iPhone 的正常使用,但当你想查看一些东西时,它会让你轻松地访问辅助工具。

控制中心

切换辅助功能的第二种快速访问方式是通过控制中心,尽管这种方式比较有限。从屏幕顶部向下滑动,或者在带有 home 按钮的设备上从下向上滑动,将显示一串按钮,提供对常用设备控制的快速访问(图 7-5 )。显示的控件可通过您设备的设置应用进行自定义。跟随➤控制中心定制控制。此处可用的辅助功能控件有黑暗模式、引导式访问和文本大小。还有一个选项,可以用我们在上面看到的设置中选择的相同选项来切换辅助功能快捷方式。配有助听器的用户也可以在这里控制它们。

img/486920_1_En_7_Fig5_HTML.jpg

图 7-5

底部一行带有辅助功能控件的控制中心。l–R 文本大小、辅助功能快捷方式、黑暗模式和引导式访问

引导访问

引导式访问允许设备仅锁定到当前应用,并禁用某些应用和系统功能。引导式访问使设备成为独立的信息亭设备,通常用于零售场所,在这些场所,您可能不希望让公众不受限制地访问设备。然而,引导式访问主要是为了帮助有各种不同需求的人:那些有学习困难的人很容易被新事物弄糊涂,或者不能完全理解特定行为的后果。患有运动障碍的人有时会导致意外输入,然后无法将设备恢复到他们可以使用的状态。患有注意力缺陷障碍焦虑症的人会被太多的刺激压垮。在所有这些情况下,限制可用的选项是可取的。

需要在设备的辅助功能设置中启用引导式访问。启用后,可通过按下侧面或主屏幕按钮三次的辅助功能快捷方式激活(图 7-6 )。引导式访问针对每个应用会话启用,必须禁用才能退出应用。

img/486920_1_En_7_Fig6_HTML.jpg

图 7-6

设置引导式访问

您可以通过检查isGuidedAccessEnabled来检测引导式访问是否被激活,并在该设置改变时从guidedAccessStatusDidChangeNotification(列表 7-1 )接收更新。在您的应用中,您可以使用此设置的状态来决定是否锁定单个功能,如设置或破坏性操作。

import UIKit

class MyViewController: UIViewController {

    var guidedAccessStatus: Bool {
        get{
            return UIAccessibility.isGuidedAccessEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(guidedAccessChanged), name: UIAccessibility.guidedAccessStatusDidChangeNotification, object: nil)
    }

    @objc
    func guidedAccessChanged() {
      // check guidedAccessStatus for current status.
      // Hide features as appropriate.
    }
}

Listing 7-1Registering for notifications in guided access status

利用这一点的更好方法是在应用启动时实现UIGuidedAccessRestrictionDelegate。此委托允许您设置自定义操作,当客户在您的应用中启用引导式访问时,可以根据请求启用或禁用这些操作。例如,您可以为“设置”或“删除项目”添加限制这可以由设置引导访问的人来配置,以使引导访问用户能够根据他们的偏好来定制设置,但是不允许他们删除任何项目。

首先,我们需要为用户可能想要禁用的每个特性创建唯一的字符串。将这些创建为 enum(清单 7-2 )允许我们保持这种类型的安全,并提供我们需要的额外数据。

enum Restriction: String, CaseIterable {
    case settings = "com.myCompany.myApp.restriction.settings"
    case delete = "com.myCompany.myApp.restriction.delete"
}

Listing 7-2Providing unique strings for Guided Access features

接下来,我们需要在 iOS 上向客户显示人类可读的字符串——一个用作按钮标签的短字符串,然后是一个较长的描述性字符串。让我们扩展清单 7-3 中的枚举,将这些值与唯一的字符串关联起来。

extension Restriction {
    var title: String {
        switch self {
        case .settings:
            return "Settings"
        case .delete:
            return "Delete"
        }
    }

    var detail: String {
        switch self {
        case .settings:
            return "Allow changing settings"
        case .delete:
            return "Allow permanent deletion of items"
        }
    }
}

Listing 7-3Associating human-readable strings with our restrictions enum

现在我们需要通过符合UIGuidedAccessRestrictionDelegate(清单 7-4 )在我们的应用委托中向 iOS 提供这些字符串。我们需要遵循两个协议方法和一个变量。guidedAccessRestrictionIdentifiers变量是一组唯一的字符串,供 iOS 和我们的应用用来识别功能。然后我们有两个函数,textForGuidedAccessRestrictiondetailTextForGuidedAccessRestriction,在这里我们提供我们人类可读的字符串。为了让我们的应用委托更整洁,让我们在扩展中这样做。

extension AppDelegate: UIGuidedAccessRestrictionDelegate {
    var guidedAccessRestrictionIdentifiers: [String]? {
        return Restriction.allCases.map { $0.rawValue }
    }

    func textForGuidedAccessRestriction(withIdentifier restrictionIdentifier: String) -> String? {
        return Restriction(rawValue: restrictionIdentifier)?.title
    }

    func detailTextForGuidedAccessRestriction(withIdentifier restrictionIdentifier: String) -> String? {
        return Restriction(rawValue: restrictionIdentifier)?.detail
    }
}

Listing 7-4Providing our Guided Access strings to iOS

最后,当用户改变某个特性的引导访问状态时,我们需要处理 iOS 的回调(清单 7-5 )。为此,我们需要在我们的扩展中符合另一个委托函数,guidedAccessRestriction(withIdentifier restrictionIdentifier: didChange:)。这个函数为我们提供了一个.allow.denynewRestrictionState枚举值。

    extension AppDelegate: UIGuidedAccessRestrictionDelegate {

...

func guidedAccessRestriction(withIdentifier restrictionIdentifier: String,
didChange newRestrictionState: UIAccessibility.GuidedAccessRestrictionState) {

        switch restrictionIdentifier {
        case Restriction.settings.rawValue:
            if newRestrictionState == .deny {
                // remove settings feature
            } else {
                // add settings feature
            }

        case Restriction.delete.rawValue:
            if newRestrictionState == .deny {
                // remove delete feature
            } else {
                // add delete feature
            }
        default:
            preconditionFailure()
        }
    }
}

Listing 7-5Handling user changes in Guided Access feature status

此外,您的应用可以随时查询UIAccessibility API 上的guidedAccessRestrictionState(forIdentifier: String)函数,以确定限制的状态(清单 7-6 )。这可以用来决定是否拒绝一个动作,或者更好的是,完全隐藏一个选项。

import UIKit

class MyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let deleteFeatureState = UIAccessibility.guidedAccessRestrictionState(forIdentifier: Restriction.delete.rawValue)

        switch deleteFeatureState {
        case .allow:
            // enable the delete feature
        case .deny:
            // disable the delete feature
        @unknown default:
            preconditionFailure()
        }
    }
}

Listing 7-6Detecting the current status of a Guided Access restriction

本地化

整个 iOS 都在构建本地化。虽然将你的应用翻译成不同的语言可能会很复杂和微妙,但支持这一点的编码并不复杂。你可能觉得你的应用不需要本地化,因为你的业务目前只在一个国家可用,但这并没有反映我们的全球社会。在任何市场中,都有相当一部分人不会把市场的主要语言作为他们的第一语言。此外,从代码中删除硬编码的字符串通常有利于代码健康。

您可能会发现您的应用已经设置为开始本地化,即使您还没有本地化任何内容。但是要检查,请转到您项目的设置,并滚动到底部(图 7-7 )。在这里,您可以找到本地化部分。确保“使用基础国际化”被选中,您就可以开始了。

img/486920_1_En_7_Fig7_HTML.jpg

图 7-7

Xcode 项目的本地化设置

在这里,您可以按+按钮添加新的语言本地化,并开始翻译您的应用。若要将应用的字符串发送给翻译人员,请前往 Xcode 的编辑器菜单,然后选取“导出以进行本地化”。这将创建翻译需要的所有文件。当你得到你的翻译回来,访问同一个菜单,并选择进口本地化。但是,在此之前,您需要对您的应用进行一些更改。

视图

任何 xib 或故事板文件都可以本地化,这不仅仅意味着字符串。

使用 autolayout 对于本地化是必不可少的,正如它对于动态类型一样。不可能保证字符串的可视长度适用于每个本地化版本。许多语言比英语占用更多的空间。在德语中,英语的 12 个字符本地化为 13 个字符的 Lokalisierung,而在简体中文中,则为 3 个字符本土化。您可以使用伪语言来测试这一点;我们将在第十一章中对此进行介绍。

也考虑到一些语言,例如阿拉伯语,是从右向左书写的。iOS 可以很好地翻转你的 UI,但这是基于 autolayout 约束的。因此,未能完全解决这些问题可能会产生一些奇怪的影响。最后,确保你的文本对齐正确。如果这确实是你每次体验的意图,只使用左对齐文本。对于大多数用途,您应该使用自然对齐。这在大多数语言中从左到右显示文本,但在需要时会切换到从右到左。

当通过按下图 5-3 中的+按钮创建新的本地化时,Xcode 会显示一个对话框,询问您希望对项目中的每个视图文件执行什么操作。您有两个选项–一个新的视图文件和一个本地化的字符串文件。对于大多数用途,本地化字符串文件将是首选选项。关于如何工作的更多信息,请参见下一节关于字符串的内容。

更有效的方法是创建一个新的视图文件。这将复制现有文件,但允许您对视图进行修改,而不仅限于文本值。您可以使用这些文件来修改视图对于比您的开发语言短得多或长得多的语言的工作方式,或者那些以不同方向编写的语言的工作方式。请注意,您对一个视图文件所做的任何更改都不会反映在任何其他视图文件中,这意味着如果您支持五种本地化,那么您必须更改五个界面构建器文件。好消息是不同的本地化可以有不同的本地化方法。例如,可以用字符串文件支持四种本地化,而用新的界面构建器文件只支持一种本地化。

创建新的视图文件时,默认情况下这些文件不会本地化。如果您决定本地化,请在导航器中选择文件,并打开文件检查器。按下本地化按钮(图 7-8 )生成该视图的本地化版本。

img/486920_1_En_7_Fig8_HTML.jpg

图 7-8

Xcode 文件检查器的“本地化”按钮

用线串

并非你的应用的所有字符串都会嵌入到故事板中。对于这些,您需要创建一个 Localizable.strings 文件。访问文件➤新➤文件,并选择了一个字符串文件。称之为 Localizable.strings

该文件是应用中使用的字符串的键值对的集合。密钥可以是您希望的任何字符串格式,但通常最好在应用中保持格式一致。试着一眼就看出这是一把钥匙,而不是一根绳子。并尝试阐明字符串的使用位置和用途。出于这些原因,我更喜欢首先使用所有大写字母命名屏幕,然后是字符串的用途,用句点分隔的格式(清单 7-7 )。用分号结束每一行,即使你使用的是 Swift。

"DETAIL_PAGE.TITLE" = "Listing Detail";

Listing 7-7A localized value of “Listing Detail” used as the title for the app’s detail page

要在代码中使用这些字符串,使用清单 7-8 中的NSLocalizedString。这将字符串键作为参数和注释。Xcode 向您的翻译人员提供注释,指导他们如何最好地翻译意思。请注意,如果找不到您的本地化字符串,密钥就是显示给客户的字符串。因此,您可以选择传递一个 value 属性来提供默认值。

        pageTitle.text = NSLocalizedString("DETAIL_PAGE.TITLE",
      value: "Listing Detail",
      comment: "Header for the listing page")

Listing 7-8Setting the pageTitle UILabel’s text using a localized string in code

Localizing a Project

让我们本地化一个现有的项目。如果你还没有,克隆这本书的 GitHub repo。在 Xcode 中打开练习 7-1。如果尚未选择,请从项目导航器顶部选择项目文件。然后在编辑器窗格中选择项目。

img/486920_1_En_7_Fig9_HTML.jpg

图 7-9

查找演示项目的本地化设置

您会注意到已经有两个本地化版本(图 7-9 )。基本本地化-默认;英语——我的发展语言。让我们继续添加另一个。我将使用 Google Translate 获取德语值;你也可以选择你喜欢的语言。

警告切勿使用自动化服务翻译您的应用;始终使用专业的翻译服务。

如图 7-10 所示,按下本地化下方的+按钮并选择德语(de)。

img/486920_1_En_7_Fig10_HTML.jpg

图 7-10

更改您的方案的语言

我们将使用字符串文件,因此在下一个屏幕上(图 7-11 ,确保两个故事板文件都选择了可本地化字符串选项。

img/486920_1_En_7_Fig11_HTML.jpg

图 7-11

选择所有要本地化的文件,然后选择“可本地化的字符串”

在左侧的项目导航器中,两个文件旁边都出现了一个显示指示器。打开 Main.storyboard 旁边的文件,注意有两个子文件(图 7-12 ),一个基础本地化脚本和一个标记为 German 的新字符串文件。

img/486920_1_En_7_Fig12_HTML.jpg

图 7-12

项目导航器显示了我们本地化的故事板

首先,让我们看看故事板文件,看看我们有什么字符串。该应用用于预订太空度假(图 7-13 ),每个详细页面都为我们提供了关于我们可以访问的太空目的地的信息,并附有一张照片。将出现在这个视图中的大部分文本将根据列表而变化,所以我们没有将它们包含在这个故事板文件中。我们希望跨目的地重用的唯一字符串是“Destination”

img/486920_1_En_7_Fig13_HTML.jpg

图 7-13

我们的假日列表应用显示了我们需要本地化的字符串

打开新的字符串文件。Xcode 已经为故事板中唯一的字符串生成了一个键值对。在这里,将目的地一词替换为“Ziel”

其余的字符串是在代码中动态设置的,因此,我们需要一个本地化的字符串文件。转到文件➤新➤文件,并选择一个字符串文件。将此命名为 Localizable.strings。我们需要将三个字符串从 ViewController.swift 移动到该文件中。使用合适的键添加这些字符串,如清单 7-9 中的示例。

"DETAIL_PAGE.MARS.HEADING" = "Mars";

Listing 7-9An entry in a localizable strings file

现在我们需要告诉视图控制器使用这些新的本地化字符串。类似于清单 7-10 中的内容。

     pageTitle?.text = NSLocalizedString("DETAIL_PAGE.MARS.HEADING",
     value: "Mars",
     comment: "Mars planet name")

Listing 7-10Accessing a localized string in code

对于另外两个字符串,请务必遵循上面的两个步骤,为每个字符串指定自己的标识符。一旦你完成了这些,我们需要为我们的字符串文件创建一个德语版本。在项目导航器中选择文件,并打开文件检查器。按下“本地化…”按钮(图 7-8 )。Xcode 然后会询问现有文件属于哪个本地化版本。选择英语并按本地化。回到文件检查器,你会注意到本地化按钮已经消失了,取而代之的是一个活动本地化列表,在英语旁边有一个复选标记。查德语。将为您创建一个内容与原始文件相同的新文件。在这里,我们可以开始添加新的德语本地化字符串。

德语中的火星就是火星。因此,让我们删除标题的键值对,因为我们将只使用默认值。将副标题值替换为“地球”,并替换描述。我将让您在这里创建自己的翻译。

要在不更改设备或模拟器设置的情况下用德语运行您的应用,请编辑您目标的方案(图 7-14 )。在运行➤选项下,有一个应用语言的下拉菜单(图 7-15 )。

img/486920_1_En_7_Fig15_HTML.jpg

图 7-15

使用应用语言选项更改应用的运行语言

img/486920_1_En_7_Fig14_HTML.jpg

图 7-14

编辑你的应用方案以更改运行语言

把这个换成德语,关上窗户。下次运行时,您的应用将显示您的新德语翻译。

摘要

  • 阅读苹果的人机界面指南。这是让你的应用在 iOS 上对所有用户都有宾至如归的感觉的最后一句话。遵循苹果在这里提出的建议,你就更有可能让你的应用出现在 App Store 上,而不太可能被拒绝应用审查。

  • 在继续阅读图书之前启用辅助功能快捷方式,即使在阅读完毕后也保持启用状态。它可以让你快速访问流行的可访问性工具,并有助于无障碍测试。

  • 为本地化准备您的应用是一个很好的实践,因为它从您的代码中删除了硬编码的字符串,并使将来更改它们变得更容易。如果你决定本地化你的应用,大量的工作已经完成。

  • 即使你的应用只在一个市场可用,你仍然可以通过本地化你的应用来扩大你的市场。

在接下来的三章中,我们将更深入地探讨 Apple 为有特殊残疾或需求的人而创建的辅助功能。下一章将涵盖最广泛的功能;视觉考虑。

八、iOS 辅助功能——视觉

以下功能是 Apple 主要为帮助有视觉障碍的人而设计的。这些功能帮助盲人和低视力用户,包括远视、低视力、色觉障碍、失明等。有些配置对患有认知障碍、多动症、识字率低的人,或者喜欢文本大一点或颜色柔和一点的人也有帮助。有些人启用灰度彩色滤镜只是因为它看起来很酷。视觉因素构成了目前 iOS 上可用的最大一组辅助功能。

画外音

开发者最熟悉的 iOS 辅助功能是 VoiceOver。VoiceOver 是苹果的内置屏幕阅读器,在所有基于屏幕的平台上都有,可以与你的应用配合使用,而不需要开发者启用它的使用。VoiceOver 不仅仅是一个基本的屏幕阅读器;它还可以作为弱视用户的导航工具,使他们不仅能够知道屏幕上有什么文本,还能知道他们可以使用什么按钮或动作。iOS 还具有更基本的屏幕阅读器,仅用于阅读内容;这种屏幕阅读器在 iOS 中被称为语音内容,我们将在本章后面的“朗读选择”和“朗读屏幕”部分对此进行介绍

Caution

在您阅读“使用 VoiceOver 导航”之前,请不要启用 VoiceOver

VoiceOver 将改变你的应用的功能,因为你的用户将采用一系列的滑动来以自然的方向导航你的应用(在英语中是从左上角到右下角)。在屏幕上绘制一个边界框,为当前选择的元素增加一个视觉亮点(图 8-1 )。选择后,VoiceOver 会按以下顺序向用户朗读元素信息:辅助功能标签➤辅助功能值➤辅助功能特征➤辅助功能提示。这四个值中的每一个都是为你确定的,作为你的应用的辅助功能用户界面的一部分,在第六章中有所涉及,如果 iOS 不能确定理想的,或者实际上任何一个值,你都可以定制。

img/486920_1_En_8_Fig1_HTML.jpg

图 8-1

VoiceOver 选择放大镜。双击屏幕上的任意位置来激活此控件

虽然 VoiceOver 会为您做大量的工作,但创建与 VoiceOver 不理想兼容的界面是很容易的。常见的陷阱包括缺少或不正确的可访问性值、标签或特征;以非逻辑顺序访问的元素;VoiceOver 无法通过滑动访问或离开元素的陷阱;和 VoiceOver 与您的用户界面不同步。由于这些原因,在创建屏幕时使用 VoiceOver 检查屏幕是非常重要的,以确保您可以按预期导航。也请记住,VoiceOver 用户不会像视力正常的人那样拥有全屏环境;它们唯一的上下文是当前选中的元素,所以每个元素必须有自己的意义。第六章中涉及的语义视图是这个上下文问题的一个很好的解决方案。

您可以通过检查isVoiceOverRunning来检查 VoiceOver 当前是否正在运行,并根据清单 8-1 中的示例通过监听voiceOverStatusDidChangeNotification来确定此状态是否发生变化。

import UIKit

class MyViewController: UIViewController {

    var voiceOverStatus: Bool {
        get{
           return UIAccessibility.isVoiceOverRunning }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(voiceOverChanged), UIAccessibility.voiceOverStatusDidChangeNotification, object: nil)
    }

    @objc
    func voiceOverChanged() {
      // check voiceOverStatus for the current status.
    }
}

Listing 8-1Detecting VoiceOver status and changes

屏幕更新

UIKit 中一个常见的 VoiceOver 陷阱是在 iOS 将屏幕推至视图层次后更新屏幕上的元素。VoiceOver 并不总是知道您的视图已经更改,因此它可能不会找到新元素或可能会阅读旧内容。在 SwiftUI 中,这不再是一个考虑因素,但是对于 UIKit,您应该总是向UIAccessibility发布通知。收到此通知后,可访问性 API 将重新构建可访问的用户界面,以将更改考虑在内。如果你改变了一个元素,发布layoutChanged通知(列表 8-3 ,或者,对于屏幕的更大区域,发布screenChanged(列表 8-2 )。对于这些通知中的每一个,您都可以包含一个参数。此参数可以是 VoiceOver 要宣布的字符串,也可以是 VoiceOver 应该关注的元素。

func deleteItem(item: Item) {
     // handle removing the item from your screen.

     ...

     // VoiceOver announces "One item removed" and the accessibility user interface is recreated to reflect this.
     // VoiceOver focus is unchanged, unless the focus was on the removed element.
     UIAccessibility.post(notification: .layoutChanged, argument: "One item removed")
}

Listing 8-3Posting VoiceOver Change Notification for a screen layout change

func updateSearchResults(results: Results) {
      // update your table view.

      ...

      let firstCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0))

      // Focuses VoiceOver on the first cell.
      UIAccessibility.post(notification: .screenChanged, argument: firstCell)
}

Listing 8-2Posting VoiceOver Change Notification for a major screen change

也可以在 UI 不变的情况下,直接向 VoiceOver 发送公告来响应事件。例如,在清单 8-4 中,一个通常不会打断用户的警告或错误,比如你可以选择在哪里显示一个 toast。

func reachabilityFailed() {
     // display a toast informing of poor network connectivity.

     ...

     // VoiceOver announces "Warning: Poor network connectivity detected".
     // No changes are made to the accessibility user interface or VoiceOver focus.
     UIAccessibility.post(notification: .announcement,
     argument: "Warning: Poor network connectivity detected")
}

Listing 8-4Posting a VoiceOver announcement

以这种方式发布通知将会中断任何当前的发言。所以如果你只在绝对必要的时候使用这种技术是最好的。另一种方法是,我们可以告诉 VoiceOver,我们希望它仅在使用属性字符串(清单 8-5 )完成当前发言后发出通知。

func reachabilityFailed() {
     // display a toast informing of poor network connectivity.

     ...

     let announcement = NSAttributedString(string: "Warning: Poor network connectivity detected", attributes: [.accessibilitySpeechQueueAnnouncement: true])

        UIAccessibility.post(notification: .announcement, argument: announcement)

Listing 8-5Posting a queued announcement

作为回报,UIAccessibility在阅读完您的公告后,会向notificationCenter发送一个名为UIAccessibility.announcementDidFinishNotification的通知。

属性化可访问性字符串

就像可视化显示富文本的属性化字符串一样,我们也可以向可访问性字符串添加属性。这些属性不是控制颜色和下划线之类的东西,而是控制说话时是否包含标点之类的东西。组成可访问性用户界面的每个标签、提示和值都可以有属性。这些属性是accessibilityAttributedLabelaccessibilityAttributedHintaccessibilityAttributedValue。我们还可以将属性字符串传递给我们上面提到的 VoiceOver 通知。我们可以添加的属性如下。

accessibilitySpeechPitch(列表 8-6 )键允许我们提供一个从 0.0 到 2.0 的NSNumber浮点值。此值调整 VoiceOver 用来朗读此文本的音高。1.0 代表用户选择的画外音音高,< 1.0 降低音高,> 1.0 提高音高。这对于强调一段话很有用。

let attributedString = NSMutableAttributedString(string: "This is the best app on the App Store!")

// Always localize your strings and perform proper range calculations. I'm hard-coding values here for brevity.
let range = NSRange(location: 12, length: 4)
        attributedString.addAttributes([.accessibilitySpeechPitch: 1.5], range: range)

appDescription?.accessibilityAttributedLabel = attributedString

Listing 8-6Adjusting VoiceOver pitch for emphasis

语言

这个键accessibilitySpeechLanguage(列表 8-7 ,允许我们指定一个 BCP 47 1 语言键来定义用于发音字符串的语言规则。这可以用于为非本地单词提供更准确的发音。

spanishGreeting?.accessibilityAttributedLabel = NSAttributedString(string: "Hola!", attributes: [.accessibilitySpeechLanguage: "es-419"])

Listing 8-7Setting VoiceOver language for an foreign language word

拼出

单独读出字符串中的每个字符。从我自己的测试来看,这个在 iOS 13 上用大写的单词玩的并不好。

当您的应用包含账号或电话号码等数字时,accessibilitySpeechSpellOut(列表 8-8 )键非常有用。当 VoiceOver 遇到数字时,它会决定是将这些数字读为整数还是数字;根据我的经验,这个决定并不总是正确的。应用此属性意味着 VoiceOver 将始终单独朗读每个插图。

let attributedString = NSMutableAttributedString(string: "Your account number is 12345678")

// Always localize your strings and perform proper range calculations. I'm hard-coding values here for brevity.
let range = NSRange(location: 23, length: 8)
        attributedString.addAttributes([.accessibilitySpeechSpellOut: true], range: range)

accountNumber?.accessibilityAttributedLabel = attributedString

Listing 8-8Telling VoiceOver to read an account number as digits

注音符号

VoiceOver 并不总是每个单词都有正确的发音,例如,如果这个单词是一个品牌名称,就更是如此。使用国际音标,或国际音标,我们可以指定画外音如何发音。这里的关键是accessibilitySpeechIPANotation(列表 8-9 )。

brandName.accessibilityAttributedLabel = NSMutableAttributedString(string: "[air-bee-an-bee]", attributes: [.accessibilitySpeechIPANotation: true])

Listing 8-9Using IPA notation to specify pronunciation of AirBnB

标点

如果您的应用包含代码或其他一些标点符号很重要的文本,您可以使用accessibilitySpeechPunctuation(列表 8-10 )来强制 VoiceOver 读出每个标点符号。

helloWorld.accessibilityAttributedLabel = NSMutableAttributedString(string: "print(\"Hello, World!\")", attributes: [.accessibilitySpeechPunctuation: true])

Listing 8-10Requesting VoiceOver to announce punctuation for code

神奇水龙头

Magic Tap 是辅助功能用户快速访问屏幕上重要操作的一种方式。例如,在计时器应用中,Magic Tap 启动或停止计时器。VoiceOver 用户通过用两个手指双击屏幕上的任何位置来执行神奇的点击。您可以在视图控制器或子视图中添加一个神奇的 Tap。将它添加到一个子视图意味着只有当这个视图被聚焦时才执行这个动作。魔术 Tap 的标准用法是将它添加到视图控制器中;这样,无论焦点中的元素是什么,操作都将被执行。

通过覆盖accessibilityPerformMagicTap()函数来支持魔术点击手势(清单 8-11 )。然后你应该调用你认为你的屏幕的主要目的。然后返回 true 或 false,如果这不可能的话。

override func accessibilityPerformMagicTap() -> Bool {
      // perform your screen's main action

      return true // return false if the action failed
}

Listing 8-11Supporting Magic Tap

Navigating With Voiceover

VoiceOver 不需要先启用;一旦您在“设置”中打开此选项,VoiceOver 就会被激活。VoiceOver 改变了 iPhone 的导航方式,因此在不知道如何先禁用它的情况下,不要打开它并四处轻敲,这一点很重要。很多次,当我在苹果经销商处工作时,客户会带来几天没用的 iPhones,因为闲置的拇指让他们启用了 VoiceOver,但他们无法再次关闭它。我甚至听说有人为了让手机失效而彻底擦拭手机。

Voiceover 会按照用户语言设置的自然顺序(英语为从左上方到右下方)检测辅助元素,并一次高亮显示一个元素。单次点击不再激活项目,就像你在点击按钮时期望的那样,而是点击导致元素被读取。屏幕也可以通过滑动来导航,因为元素可能不总是可见或足够大,以至于弱视的人无法准确点击。因此,向右滑动会移至右下方的下一个元素;向左滑动将会向左上方导航。该元素将被激活并被读取。

激活一个元素——拨动开关,点击按钮等。–手势现在变成双击。无论您点击屏幕上的哪个位置,也无论您点击时手指下面是什么,按钮都会对双击做出响应。这对于有视觉障碍的人来说是有益的,因为他们可能很难确定点击目标的准确位置——因此消除这一要求意味着更精确的控制激活。它也方便了纱窗的使用。屏幕幕布完全遮住了 iPhone 的屏幕,所以在其他人看来,这款手机就像睡着了一样。除了节省电池,这还为盲人和视力受损的用户提供了额外的隐私。除了大声朗读他们的个人内容,盲人用户可能不知道屏幕的全部内容。这最终可能会无意中向周围的人显示敏感的个人信息。

img/486920_1_En_8_Fig2_HTML.jpg

图 8-2

VoiceOver 转子控制选择标题

转子控制(图 8-2 )可用于配置辅助 VoiceOver 手势的行为方式。垂直滑动而不是水平滑动将执行转子上选择的功能。最常见的用途之一是标题选项,当在屏幕上垂直滑动时,该功能将跳过内容,只阅读带有标题可访问性特征的元素。这有助于浏览屏幕内容,而不必浏览每个元素。其他转子控制可让您通过辅助操作、容器来导航,控制语速,或通过朗读单个字符或单词来拆分文本。

一款云视频会议软件

屏幕缩放对那些视野狭窄的人很有用。但是,如果您的动态文本支持不够,或者 voiceover 不清楚屏幕上的内容,您可能会发现其他人也在使用它。当您执行用户测试时,如果您发现有视觉障碍的参与者在特定的屏幕上启动缩放,您可能会发现这是因为您需要在这里进行其他的可访问性改进。

img/486920_1_En_8_Fig3_HTML.jpg

图 8-3

在窗口模式下启用缩放

变焦可用于两种模式中的一种:首先是窗口变焦,取景器出现在屏幕上,放大下面的内容(图 8-3 )。这类似于在屏幕上使用放大镜,但不会显示您在现实世界中使用放大镜看到的底层 RGB 像素。其次,全屏缩放,即整个屏幕被缩放,不再适合整个设备的显示。

这是让它成为一些视障用户的第二选择的原因——这两种模式都可以有效地将显示给客户的内容减少到全屏显示的四分之一左右。这意味着四分之三的屏幕内容不再可见,最明显的是非自然一侧的内容(右为英语)。要使用缩放,你需要在屏幕上移动手指来跟随内容,这使得元素很容易被错过,并且可以看到的内容失去了上下文。内容也会变得像素化和模糊,因此更难确定。

缩放还具有 HUD(图 8-4 )功能,可以轻松控制缩放区域——全屏或窗口,轻松定位缩放区域,能够更改缩放级别,并可以选择仅向缩放区域添加颜色滤镜。对于那些发现彩色滤镜有助于他们查看内容,但不要求滤镜总是处于启用状态的人来说,最终选择是最佳选择。

img/486920_1_En_8_Fig4_HTML.jpg

图 8-4

缩放 HUD 选项

默认情况下,屏幕缩放将跟随屏幕上的焦点,这意味着如果您的客户正在填写表单,缩放将随着光标的移动而移动到下一个文本字段。有时,特别是如果您使用自定义控件,这可能不会像您希望的那样无缝地发生。在这种情况下,您可以使用zoomFocusChanged(zoomType: .insertionPoint, toFrame: myCGRect, in: myUIView)(列表 8-12 )通知 Zoom 应该将焦点改变到您屏幕的某个区域,其中myCGRect是要聚焦的区域。

import UIKit

Class MyViewController: UIViewController {

   @IBOutlet private var myCustomView: CustomView!

   override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)

      UIAccessibility.zoomFocusChanged(zoomType: .insertionPoint, toFrame: myCustomView.frame, in: view)
   }
}

Listing 8-12Moving Zoom focus to a view on viewDidAppear

要在屏幕上移动缩放区域,需要使用三指滑动,这意味着如果你在应用中使用三指手势,这些手势不会被 zoom 使用,也不会传递到你的应用。与大多数辅助功能不同,Apple 没有为您的应用提供属性来确定缩放是否已启用,以及在需要时更改行为。相反,你可以使用屏幕上的registerGestureConflictWithZoom()来提示 iOS 呈现系统警告(图 8-5 ),通知你的客户缩放可能会与你的应用的功能冲突;此警报允许您的用户禁用缩放。

img/486920_1_En_8_Fig5_HTML.jpg

图 8-5

当调用registerGestureConflictWithZoom()且用户启用缩放时,出现缩放手势冲突对话框

粗体文本

粗体文本如其在锡上所言(图 8-6 )。如果你正在使用UIFontTextStyles,你的文本将免费获得这个。如果你已经选择不使用苹果的文本样式,你应该听听isBoldTextEnabledboldTextStatusDidChangeNotification(列表 8-13 )并切换到粗体字体或根据要求添加粗体属性。您可能还想考虑切换任何资源或增加边框的粗细,以使元素更加突出。

img/486920_1_En_8_Fig6_HTML.jpg

图 8-6

带有标准文本(左)和粗体文本(右)的 iOS 文本样式

import UIKit

class MyViewController: UIViewController {

    var boldTextStatus: Bool {
        get{
            return UIAccessibility.isBoldTextEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(boldTextChanged), name: UIAccessibility.boldTextStatusDidChangeNotification, object: nil)
    }

    @objc
    func boldTextChanged() {
      // check boldTextStatus for current status.
      // switch to bold fonts or assets as appropriate
    }
}

Listing 8-13Detecting Bold Text status changes

较大的文本或动态类型

更大的文本是我们作为开发人员称之为动态类型的友好的可读名称。动态类型允许用户将文本大小调整到更适合他们使用设备的大小(图 8-7 )。从最佳辅助功能的角度来看,这不仅仅是关于需要更大的文本来阅读屏幕的人,而是关于定制。此功能允许文本大小低于默认大小,从而无需滚动即可在屏幕上显示更多内容。范围从正文大小为 14pt 的 xSmall (extra small)到正文大小为 53 的 AX5 (accessibility size 5)。参见表 8-1 了解正文字体大小的完整范围。

表 8-1

动态文字大小

|

动态字体大小

|

正文大小(磅)

|
| --- | --- |
| xSmall | Fourteen |
| | Fifteen |
| 中等 | Sixteen |
| 大(默认) | Seventeen |
| xLarge | Nineteen |
| xx 大 | Twenty-one |
| | Twenty-three |
| ax 1 | Twenty-eight |
| ax 2 | Thirty-three |
| ax 3 | Forty |
| ax4 | Forty-seven |
| AX5 | Fifty-three |

作为 HIG 的一部分,可提供与文本样式匹配的各种动态文本大小。 2

动态字体与 iOS 的内置文本样式配合使用时效果最佳。这些样式告诉 iOS 和动态类型系统特定文本的语义用法是什么,这意味着您的所有文本都按照苹果推荐的间隔按比例缩放。这里显示了不同大小设置的文本样式的完整选项。

img/486920_1_En_8_Fig7_HTML.jpg

图 8-7

标准尺寸(左)、最小尺寸(中)和最大辅助功能尺寸(右)的动态文字样式

作为一般规则,你的所有文本应该使用这些内置文本样式之一,清单 8-14 展示了如何在 UIKit 中实现这一点。在 SwiftUI 中,所有文本都支持动态类型,默认样式为body。可以使用.font()修饰符(列表 8-15 )选择其他样式,并传递一个动态类型样式。传递一个定义好的大小,比如.system(size: 17),会禁用动态类型,防止缩放,所以应该避免。

// For body style, no font modifier is required.
Text("Example text").font(.headline)

Listing 8-15Creating labels with Dynamic Type in SwiftUI

let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

Listing 8-14Creating labels with Dynamic Type in UIKit

要在界面构建器中启用动态类型(图 8-8 ,打开属性检查器,并在字体属性框中选择“T”图标。然后单击出现的新字体下拉菜单,选择您选择的文本样式。最后,选中下面标有“自动调整字体”的框

img/486920_1_En_8_Fig8_HTML.jpg

图 8-8

在界面构建器中选择动态文字样式

自定义样式

如果您的设计需要一种不同于 iOS 默认字体 San Francisco 的字体,该怎么办?或者,如果您希望您的标准正文文本大小大于 17 呢?我们可以使用 iOS' UIFontMetrics(清单 8-16 )来做到这一点。我不会在这里介绍给你的应用添加自定义字体,但是苹果在他们的开发者文档中提供了一个简单的指南。准备好自定义字体后,在代码中创建一个字体实例,当文本设置为标准大小时,使用您希望的字体和大小。创建一个使用该字体的文本样式的UIFontMetrics实例,然后将你的自定义字体实例传递给你的UIFontMetrics实例。

let font = UIFont(name: myCustomFontName, size: defaultSize)
let bodyMetrics = UIFontMetrics(forTextStyle: .body)
label.font = bodyMetrics.scaledFont(for: font)
label.adjustsFontForContentSizeCategory = true

Listing 8-16Creating a dynamic text style with a custom font

以这种方式创建文本样式可确保您尊重客户选择的文本大小。此外,Apple 会为您配置行距、段落间距和其他指标,确保您的文本始终可读。

按钮形状

按钮形状通过添加不同的轮廓、背景或带下划线的文本(图 8-9 )来帮助使按钮点击目标更加可见。对于没有视觉障碍的人来说,按钮形状也使得确定屏幕上的哪些区域可以进行交互变得更加简单。如果您以任何方式自定义了按钮,有时可能会发现文本或按钮形状没有对齐。有必要启用这些功能来检查你的应用看起来是否正常,按钮标签是否可读。如果你使用一个没有从UIButton子类化的元素作为按钮,你不会得到这种行为。

img/486920_1_En_8_Fig9_HTML.jpg

图 8-9

启用按钮形状的后退导航按钮

开/关标签

对于 iOS 的开关或切换控制,苹果的设计只使用颜色来指示用户开关是开还是关,这意味着任何有视觉障碍的人都很难区分颜色,很难理解这种控制。因此,苹果公司提供了一种设置来为开关添加标签——I 在打开位置,O 在关闭位置,如图 8-10 所示。

img/486920_1_En_8_Fig10_HTML.jpg

图 8-10

启用开/关标签的开关控制

当创建你自己的定制控件时,你也应该考虑这一点,如果你选择默认不使用标签,听听isOnOffSwitchLabelsEnabledonOffSwitchLabelsDidChangeNotification(列表 8-17 )来决定什么时候添加额外的信息。

import UIKit

class MyViewController: UIViewController {

    var switchLabelStatus: Bool {
        get{
            return UIAccessibility.isOnOffSwitchLabelsEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(switchLabelsChanged), name: UIAccessibility.onOffSwitchLabelsDidChangeNotification, object: nil)
    }

    @objc
    func switchLabelsChanged() {
      // check switchLabelStatus for current status
      // add on/off labels to your controls as needed.
    }
}

Listing 8-17Detecting changes in switch label status

降低透明度

降低透明度是一些弱视用户的基本特征;对于那些视力已经模糊的人来说,在原本透明的背景上添加模糊会使区分前景和背景变得很困难。如果在构建时没有定义背景,透明度也会导致对比度低于可接受的值。

这一点最明显的用途是跳板上的文件夹(图 8-11 )。这些都是一个很好的例子,说明了如何使一个可访问性特性具有可比较的体验,而不是二等的体验。禁用此功能后,文件夹的背景为浅灰色透明。轻敲文件夹,透明的灰色文件夹 squircle 后面的背景是一个未打印的模糊跳板。启用这个设置,当访问一个文件夹时,跳板内容被删除,保留一个暗淡的跳板背景图像。文件夹呈现较暗的灰色外观,没有透明度,但是灰色背景颜色暗示了其背后的跳板背景图像的颜色。

img/486920_1_En_8_Fig11_HTML.jpg

图 8-11

跳板文件夹(左)。透明度降低的跳板文件夹(右)

默认情况下,不会像许多文本调整一样为您启用降低透明度,因为 Apple 无法根据您的 UI 设计明确决定如何降低透明度。如果你在你的设计中使用透明,你必须决定是否以及如何响应这个设置。如果您的透明图层位于一种纯色之上,并且您可以保证这种颜色在构建时不会改变,则可能根本不需要响应。在所有其他情况下,应该做出一些妥协。一个简单的决定是用纯色代替透明色;一个更好的选择可能是制作一个类似的体验,比如苹果的跳板文件夹,其中使用了背景图像的色调。

为了确定是否响应这个设置,使用 iOS 的 Accessibility API,访问isReduceTransparencyEnabled变量,并监听reduceTransparencyStatusDidChangeNotification,如下面的清单 8-18 所示。

import UIKit

class MyViewController: UIViewController {

    var reduceTransparencyStatus: Bool {
        get{
            return UIAccessibility.isReduceTransparencyEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(reduceTransparencyChanged), name: UIAccessibility.reduceTransparencyStatusDidChangeNotification, object: nil)
    }

    @objc
    func reduceTransparencyChanged() {
      // check reduceTransparencyStatus for current status.
      // add opacity to views as needed.
    }
}

Listing 8-18Detecting changes in Reduce Transparency status

增加对比度

增加对比度是UIAccessibility的较暗系统颜色选项的用户友好名称。然而,更暗的系统颜色是这个选项实际作用的一个更具描述性的名称——内置的苹果应用将使颜色变暗,特别是文本后面的颜色,以增加对比度(图 8-12 )。在“设置”中切换此选项,并查看顶部“后退”按钮的色调变化。另外,导航到消息并注意消息气泡颜色的差异。

img/486920_1_En_8_Fig12_HTML.jpg

图 8-12

增加信息的对比度(左)并关闭该功能(右)。请注意消息撰写栏中的深色文本和消息气泡上的深色背景

为了确定是否响应这个设置,使用 iOS 的 Accessibility API,访问isDarkerSystemColorsEnabled变量,并监听darkerSystemColorsStatusDidChangeNotification(列表 8-19 )。从 iOS 13 开始,这个设置也可以通过带有accessibilityContrast属性的视图特征集合来确定(清单 8-20 )。该特征返回一个值为normalhigh的枚举,反映客户的设置。

let contrast = traitCollection.accessibilityContrast
// returns an enum value of .high or .normal.

Listing 8-20Determining high-contrast status from a view’s trait collection

import UIKit

class MyViewController: UIViewController {

    var darkerColorsStatus: Bool {
        get{
            return UIAccessibility.isDarkerSystemColorsEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(darkerColorsChanged), name: UIAccessibility.darkerSystemColorsStatusDidChangeNotification, object: nil)
    }

    @objc
    func darkerColorsChanged() {
      // check darkerColorsStatus for current status
      // replace colors/assets for high contrast alternatives
    }
}

Listing 8-19Detecting if increase contrast is enabled

只要你为你的应用提供了高对比度的变体,iOS 就会为你处理颜色的切换。存储在素材目录中的图像和颜色素材都可以提供高对比度变体(图 8-13 )。打开 xcassets 文件,选择要为其创建高对比度变体的颜色或图像资源。从右侧的属性检查器中,切换“高对比度”复选框。对于每个选定的素材价值,将出现第二个变量。iOS 将根据您客户的设置自动为您选择合适的素材。在确定清单 8-11 中的设置状态后,任何没有存储在素材目录中的颜色或图像都需要在您的代码中进行切换。

img/486920_1_En_8_Fig13_HTML.jpg

图 8-13

素材目录中的普通和高对比度颜色变体

没有颜色区分

一些损伤,如色盲,会阻碍我们感知颜色的能力。因此,WCAG 的一个重要特点是以多种冗余形式向您的客户提供信息。例如,一个包含感叹号的红色警告三角形,与标签“错误”组合在一起,以三种方式向您的客户提供信息-形状、颜色和文本。

有时你的设计可能需要单独使用颜色,比如给文本标签或背景着色来暗示不同的状态。如果你的应用以这种方式使用颜色,它的意义很容易被任何对颜色有不同体验的人所遗忘。 4

为了确定你是否应该提供额外的形式来提供这些信息,请听shouldDifferentiateWithoutColordifferentiateWithoutColorDidChangeNotification,参见清单 8-21 。

import UIKit

class MyViewController: UIViewController {

    var differentiateWithoutColorStatus: Bool {
        get{
            return UIAccessibility.shouldDifferentiateWithoutColor
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(differentiateWithoutColorChanged), name: NSNotification.Name(rawValue: UIAccessibility.differentiateWithoutColorDidChangeNotification), object: nil)
    }

    @objc
    func differentiateWithoutColorChanged() {
      // check differentiateWithoutColorStatus for current status.
      // Add shapes or text to add meaning.
    }
}

Listing 8-21Detecting changes in differentiate without color status

反转颜色

反转颜色有助于提高对比度,因为大多数应用通常会在浅色背景上显示深色文本;翻转在深色背景上提供较亮的文本,使文本突出显示(图 8-14 )。它也被那些对光或颜色敏感的人使用,因为它具有减少屏幕上亮色的效果。

经典反转改变了一切,这意味着你的图像通常会看起来完全错误。通过智能反转,iOS 使用对屏幕上出现的内容的一些判断来决定是否反转颜色。一般来说,文本内容和背景是反转的,而图像和视频保持标准颜色。iOS 在这方面并不是 100%成功;一般来说,如果你把内容放在图像上,图像通常会被反转。有些屏幕根本没有变化,还有一些浅色。为了更好地了解 Smart Invert 的功能,我建议您启用该功能,并在设备上导航一段时间。注意特定图像、色调等的差异。,而许多保持不变。

img/486920_1_En_8_Fig14_HTML.jpg

图 8-14

智能反转–文本和背景反转;图像保持其原始颜色

如果您发现应用的某些部分在不反转的情况下会工作得更好,UIView 包含了一个用于accessibilityIgnoresInvertColors(列表 8-22 )的属性。此属性可以设置为 true,以确保无论用户的设备设置如何,您的颜色都保持不变。属性将防止设置此属性的视图及其所有子视图发生反转。明智地使用这个属性;在每个视图上将此设置为真可能会让设计者高兴,但可能会激怒选择启用此设置来帮助他们使用您的应用的客户。使用该属性是为了可用性,而不是为了设计。

// heroImage is a reference to a UIImageView on our screen
heroImage.accessibilityIgnoresInvertColors = true

Listing 8-22Setting an image to ignore the inverted color setting

颜色过滤器

颜色过滤器允许您的客户调整某些或所有颜色的显示方式,使那些可能在对比度或颜色方面有困难的人更容易确定不同的颜色。这些过滤器也可以用来减少颜色的活力或完全关闭颜色。不需要对您的应用进行更改,也不需要设置监听来检测这些过滤器,但值得注意的是,您的一些客户可能会选择使用这些过滤器,它们会改变您的应用的外观。

如果启用了大多数滤镜,它们不会向您的应用报告,但可以使用isGrayscaleEnabledgrayscaleStatusDidChangeNotification检测灰度(列表 8-23 )。

import UIKit

class MyViewController: UIViewController {

    var grayscaleStatus: Bool {
        get{
            return UIAccessibility.isGrayscaleEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(grayscaleChanged), name: UIAccessibility.grayscaleStatusDidChangeNotification, object: nil)
    }

    @objc
    func grayscaleChanged() {
      // check grayscaleStatus for current status.
      // Add shapes or text to add meaning.
    }
}

Listing 8-23Detecting changes in Grayscale status

减少白点

减少白点减少颜色的强度,给它们添加灰色元素;这可以减轻眼睛疲劳,减少眩光,但可能会对您的一些 UX 或图像的外观产生轻微的变化。差别是微妙的,所以如果你已经确保你的设计有足够的对比度,这不应该有负面影响。

减少运动

“减少运动”选项对于受运动影响的人来说是必不可少的,因为特定的动画可能会引发头晕和恶心。这对患有焦虑症、多动症和自闭症的人来说也是无价的。有了这些损伤,大量的运动,尤其是外围的运动,会让人分心和心烦意乱。要了解可以触发的动画类型,请在手机上启用此选项。当你使用手机时,留意苹果禁用了哪些动画,比如启动应用时的缩放动画。快速动画、多个平面中的动画和缩放动画都可以产生效果,因此如果您利用这些,请考虑在触发动画之前收听isReduceMotionEnabled,并使用替代的演示形式。该设置更改时发布的通知为reduceMotionStatusDidChangeNotification(列表 8-24 )。

import UIKit

class MyViewController: UIViewController {

    var reduceMotionStatus: Bool {
        get{
            return UIAccessibility.isReduceMotionEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(reduceMotionChanged), name: UIAccessibility.reduceMotionStatusDidChangeNotification, object: nil)
    }

    @objc
    func reduceMotionChanged() {
      // check reduceMotionStatus for current status.
      // stop or reduce the intensity of animation.
    }
}

Listing 8-24Detecting if Reduce Motion is enabled

朗读选择

当长按单词或文本选择时,朗读选择会在弹出功能区中添加一个额外的选项。除了复制、查找等标准选项之外。,出现一个发言按钮(图 8-15 )。轻按它,Siri 将只读取高亮显示的单词或选择。您可以使用isSpeakSelectionEnabled检查这是否被启用,并使用speakSelectionStatusDidChangeNotification监听该设置的变化(列表 8-25 )。

img/486920_1_En_8_Fig15_HTML.jpg

图 8-15

朗读选择

import UIKit

class MyViewController: UIViewController {

    var speakSelectionStatus: Bool {
        get{
            return UIAccessibility.isSpeakSelectionEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(speakSelectionChanged), name: UIAccessibility.speakSelectionStatusDidChangeNotification, object: nil)
    }

    @objc
    func speakSelectionChanged() {
      // check speakSelectionStatus for current status.
      // pause any audio or video playing.
    }
}

Listing 8-25Detecting changes in Speak Selection status

朗读屏幕

Speak Screen 是苹果对 iOS 内置屏幕阅读器使用的友好名称。Speak Screen 比 VoiceOver 笨得多,因为它没有任何导航或控制功能。启用时,可以用两个手指从屏幕顶部向下滑动来激活 Speak Screen(图 8-16 )。然后,Speak Screen 会识别屏幕的内容部分,并从左上至右下阅读它们,忽略任何导航栏或标签栏按钮或标题。

img/486920_1_En_8_Fig16_HTML.jpg

图 8-16

朗读屏幕

“朗读屏幕”遵循 VoiceOver 使用的相同的isAccessibilityElement属性,因此将跳过相同的视图和控制。该行为不同于 VoiceOver,因为用户无法控制阅读的内容和时间,这意味着他们不能选择跳过或重复章节。值得在你的应用上启用 Speak Screen,并在每个屏幕上激活它,以确定你的内容在以这种方式阅读时是否有意义。

可以在设置中启用朗读屏幕,而无需对设备的外观或行为进行任何更改。您可以通过检查isSpeakScreenEnabled来确定该设置是否已启用。请注意,该属性返回 true 并不意味着在您的任何屏幕上都激活了 Speak ScreenspeakScreenStatusDidChangeNotification可用于确定您的用户在使用您的应用时是否更改了该设置(列表 8-26 )。

import UIKit

class MyViewController: UIViewController {

    var speakScreenStatus: Bool {
        get{
            return UIAccessibility.isSpeakScreenEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(speakScreenChanged), name: UIAccessibility.speakScreenStatusDidChangeNotification, object: nil)
    }

    @objc
    func speakScreenChanged() {
      // check speakScreenStatus for current status.
      // pause any audio or video playing.
    }
}

Listing 8-26Detecting changes in Speak Screen status

朗读屏幕将继续阅读屏幕上的所有可用内容,直到它到达底部;然后,它会尝试滚动以找到更多要阅读的内容。如果不能滚动,则尝试在父视图上滚动,依此类推,直到没有进一步的父视图;在这一点上,阅读停止。任何自然滚动的 UIKit 元素都会为您实现这种滚动。如果你通过任何其他方式滚动——比如在电子书中翻页或者在一系列入职屏幕中从右向左滑动,你应该实现清单 8-27 中的accessibilityScroll(_ direction: UIAccessibilityScrollDirection)函数。如果滚动成功,您必须返回true并发布pageScrolled可访问性通知。

import UIKit

class MyViewController: UIViewController {

    override func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
        if lastPage() {
            return false
        }

        present(nextPage(), animated: true) {
            UIAccessibility.post(notification: .pageScrolled,
                                 argument: nil)
        }

        return true
    }

    func nextPage() -> UIViewController {
        // return your next content view controller
        return NextViewController()
    }

    func lastPage() -> Bool {
        // return true if this is the last page of this content
       if isLastPage {
          return true
       }

       return false
    }
}

Listing 8-27Accessibility Scrolling

音频描述

AVFoundation包含内置支持音频描述音轨。当您的客户启用此设置时,为您的媒体使用AVPlayer实例将免费为您提供此功能,前提是您确保您的视频中嵌入了音频描述。任何好的视频编辑软件都允许你为描述添加辅助音轨。

深色模式

黑暗模式是 iOS 13 的新功能。您的应用开箱即可支持黑暗模式,无需任何更改。但是如果你没有做出改变,你的应用可能会在启用黑暗模式时看起来有点奇怪。黑暗模式虽然看起来整洁并节省电池寿命,但对于对光敏感的人来说是一个基本的辅助功能,更高的对比度有助于改善一些视觉障碍,如色彩不足和视觉模糊。

您可以通过显示和亮度下的设置应用切换黑暗模式(图 8-17 )。现在切换黑暗模式,测试你的应用,看看你可能需要做出改变。您可能需要添加一些颜色和图像资源的深色变体。我们将在下面讨论这个问题。

img/486920_1_En_8_Fig17_HTML.jpg

图 8-17

亮模式(左)和暗模式(右)下的显示和亮度设置

可以将颜色和图像的暗(或亮)变型添加到存储在素材目录中的任何素材。选择您想要添加深色变体的颜色或图像,然后在“属性”检查器的“外观”部分中选取“任何,深色”(图 8-18 )“任何”素材是 iOS 13 之前的设备上 iOS 将使用的值,其中黑暗模式不是一个功能。通常,这与“灯光”选项相同,但是如果需要指定灯光选项以及传统资源,请选择“任何,灯光,黑暗”选项。

img/486920_1_En_8_Fig18_HTML.jpg

图 8-18

素材目录中的深色和任何颜色变体

如果你需要基于颜色模式做进一步的 UI 修改,你可以查询你的视图的特征集合(清单 8-28 )。userInterfaceStyle将返回一个枚举值.light.dark或。.unspecified针对 13 以下 iOS 版本。

let darkMode = traitCollection.userInterfaceStyle
// Returns .light, .dark, or .unspecified as an enum value.

Listing 8-28Checking Dark Mode status on a view

摘要

  • 视觉因素是目前为止 iOS 辅助功能中最大的选择,所以试图支持所有这些功能可能会令人生畏。但坚持良好实践——使用 iOS 提供的控制、布局约束和素材目录——将大有裨益。

  • 当您第一次使用 VoiceOver 时,它可能会让人感到困惑,但您会很快学会基础知识,并使它成为您开发工作流程的一部分。

  • 动态类型是任何现代 iOS 应用必不可少的一部分。如果你不支持它,你的应用在 iOS 上不会有宾至如归的感觉,你的客户也会注意到。

在下一章,我们将继续关注 iOS 的辅助功能。我们将继续讨论 iOS 为行动受限的人所做的考虑,并讨论作为一名开发人员,你可以做些什么来让这些客户在你的应用中有宾至如归的感觉。

九、iOS 辅助功能——物理和运动

身体和运动方面的考虑帮助那些与多点触摸手势斗争的人;例如,如果有人有三个手指,他们可能需要帮助才能做出四个手指的手势。对于那些几乎没有运动控制的人来说,开关控制或语音控制可以帮助他们充分利用用户的运动。

辅助触摸

辅助触摸在屏幕上呈现一个小的可移动的 home 按钮状的轻击目标(图 9-1 ,该目标扩展到轻击上的更多选项(图 9-2 和图 9-3 )。例如,这个功能对于有运动障碍的人来说非常好,他们可能很难滑动控制中心,或者缺少手指的人可能很难做捏手势。这个按钮的存在不会影响你的应用的功能,但是你也许应该意识到,有了这个按钮,你的屏幕上会有一小块区域被辅助触摸按钮遮挡。

img/486920_1_En_9_Fig1_HTML.jpg

图 9-1

辅助触摸部分遮挡内容

更值得考虑的是你使用可能需要精细运动技能的手势,如多指轻拍、轻扫、捏和其他触摸手势。如果你的应用利用了这些,考虑监控isAssistiveTouchRunning,并提供一个替代方案。您可以通过监听assistiveTouchStatusDidChangeNotification(列表 9-1 )来检测该设置的变化。

img/486920_1_En_9_Fig3_HTML.jpg

图 9-3

辅助触摸,提供对多手指手势的单击访问

img/486920_1_En_9_Fig2_HTML.jpg

图 9-2

辅助触摸提供对功能的单次点击访问

import UIKit

class MyViewController: UIViewController {

    var assistiveTouchStatus: Bool {
        get{
            return UIAccessibility.isAssistiveTouchRunning
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(assistiveTouchChanged), name: UIAccessibility.assistiveTouchStatusDidChangeNotification, object: nil)
    }

    @objc
    func assistiveTouchChanged() {
      // check assistiveTouchStatus for current status.
      // increase the prominence of gesture alternatives
    }
}

Listing 9-1Detecting changes in Assistive Touch status

摇动以撤销

摇动撤销是一个系统范围的撤销命令(图 9-4 ),相当于 Mac 上的⌘ + Z 或编辑➤撤销。对于那些在运动技能方面有困难的人来说,这个功能可能是困难的,因为他们的手可能缺少运动来完成这个手势。此外,许多人发现他们无意中触发了“摇动以撤销”,例如患有帕金森氏症的人。

img/486920_1_En_9_Fig4_HTML.jpg

图 9-4

iOS 的标准摇动撤销确认对话框。如果意外触发,这种模式对话框可能会令人沮丧

您的客户可以禁用此功能,以防止意外触发对话框而造成的挫折。如果撤销功能对你的应用来说是一个重要的特性,比如说如果你提供了文本编辑功能,你可能会希望添加一个替代的撤销机制,或者最好是增加它的重要性。你可以查询isShakeToUndoEnabled来确定是否可以摇动撤销,你可以监听shakeToUndoDidChangeNotification来改变(列表 9-2 )。

import UIKit

class MyViewController: UIViewController {

    var shakeToUndoStatus: Bool {
        get{
            return UIAccessibility.isShakeToUndoEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(shakeToUndoChanged), name: UIAccessibility.shakeToUndoDidChangeNotification, object: nil)
    }

    @objc
    func shakeToUndoChanged() {
      // check shakeToUndoStatus for current status.
      // increase the prominence of undo features.
    }
}

Listing 9-2Detecting changes in Shake to Undo status

开关控制

开关控制是为那些有更严重运动障碍的人设计的。虽然辅助触摸可以使某些手势更容易,但开关控制是为那些很难触摸或点击的人设计的。

Caution

在您阅读“使用开关控制导航”之前,请不要启用开关控制

使用 iOS 为您的应用生成的辅助功能用户界面,Switch Control 将自动扫描屏幕,用边框突出显示辅助功能元素,类似于 VoiceOver 的外观。与 VoiceOver 的不同之处在于,它不是依次高亮显示每个可访问的元素,而是将元素自动分组到有意义的区域,以减少访问任何给定元素所需的按压次数。仅包含内容的元素也被排除在外,因为该开关控件旨在作为允许控制的工具;只有可以交互的元素才会突出显示。选择一个元素后,该元素的可用动作会在下拉列表中显示(图 9-5 )。如果您使用 iOS 的内置控件,如UIButton,这些操作将为您填充。但是如果你想添加一个手势的替代,一个很好的选择是在这里添加一个可访问性自定义动作。这些细节可在第六章中找到。

img/486920_1_En_9_Fig5_HTML.jpg

图 9-5

开关控制

这种导航方法的目的是让行动受到严重限制的用户能够只使用一个手势来导航他们的设备。但是,根据用户的能力,可以添加更多的手势。有问题的开关可以是很多东西。您设备的屏幕可以制作一个单独的开关,或者可以使用为 iPhone 制作的开关或开关控制器来连接外部物理开关。这些可以通过蓝牙或 MIDI 通过 USB 连接到 lightning 适配器。对于缺乏按下或点击这些物理开关之一的运动能力的用户,可以使用设备的摄像头来检测头部运动,允许向左或向右的头部运动来控制设备的不同方面。

以这种方式导航界面可能非常耗时。因此,启用了交换机控制的用户最有可能受到超时的影响。

您可以通过监听switchControlStatusDidChangeNotification或查询isSwitchControlRunning(列表 9-3 )来检测开关控制的状态。

import UIKit

class MyViewController: UIViewController {

    var switchControlStatus: Bool {
        get{
            return UIAccessibility.isSwitchControlRunning
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(switchControlChanged), name: UIAccessibility.switchControlStatusDidChangeNotification, object: nil)
    }

    @objc
    func switchControlChanged() {
      // check switchControlStatus for current status.
      // increase the prominence of gesture alternatives.
    }
}

Listing 9-3Detecting changes in Switch Control status

导航风格

当开关控制突出显示元素时,它们通常是单独完成的。取决于布局,开关控制可以确定将元素分组在一起是合乎逻辑的,比如在分组的表格视图中。我们可以调整我们的可访问性接口来告诉 Switch Control,它应该总是将元素作为一个组或者单独处理(图 9-6 )。对元素进行分组可以更快地导航到 UI 的其他区域。当控制很重要时,单独处理它们是合适的。在这种情况下取得平衡可能很困难,理想的情况是通过对每天使用开关控制的参与者进行一些用户测试来判断。

img/486920_1_En_9_Fig6_HTML.jpg

图 9-6

使用开关控制导航。元素分别导航(左)和组合导航(右)

每个视图都有一个属性accessibilityNavigationStyle;我们可以将它设置为.separate.combined(列表 9-4 ),这取决于我们想要的结果。

buttonStackView.accessibilityNavigationStyle = .combined

Listing 9-4Setting the Switch Control navigation style to group elements presented in a Stack View

Navigating With Switch Control

在启用交换机控制之前,您需要将交换机添加到您的设备中。你的屏幕可能是在测试环境中使用的最简单的选择,尽管我也建议使用头部运动和前置摄像头来尝试一下。在辅助功能设置的开关控制菜单下,点击开关➤添加新开关… ➤屏幕➤全屏➤选择项目。

现在通过辅助功能设置启用开关控制,或者在将来通过激活我们在本节开始时设置的辅助功能快捷方式来启用开关控制。你会看到一个蓝色的边界框出现在屏幕的顶部;一秒钟后,这个框将在屏幕上向下移动到下一组可选元素;再过一秒钟,盒子就会再跳一次,以此类推。要激活突出显示的区域,请点击设备屏幕上的任意位置。如果该突出显示包含一组项目,扫描仪将开始在元素间循环。开关控制以秒为间隔完成此操作。

使用开关控制激活单个元素后,会出现一个可能操作的下拉菜单。这些操作以与屏幕本身相同的方式循环,这意味着您可以点击屏幕上的任何位置来执行高亮显示的操作,或者聚焦于高亮显示的组来导航到不同的操作。突出显示的第一个操作是“始终点击”,这意味着在没有启用开关控制的情况下使用设备时,双击元素实际上等同于直接点击元素。

禁用开关控制,方法是轻按设备上的主屏幕按钮或睡眠按钮三次以激活辅助功能快捷方式,然后在突出显示开关控制后轻按屏幕两次。

口声控制

语音控制是 iOS 13 的一项新功能,允许只使用语音来完全控制你的应用,这使它成为行动最受限的人的理想选择。语音控制使用与 VoiceOver 相同的辅助功能用户界面、开关控制和不同的辅助技术,这意味着您的应用默认支持它。和其他辅助技术一样。由 UIKit 确定的可访问用户界面可能并不总是理想的,或者确实是准确的,因此可能需要一些定制。

可以使用辅助功能快捷方式启用语音控制。但激活它最直接的方式可能是说“嘿 Siri,打开语音控制”,要禁用它,你可以简单地说“关闭语音控制”,然后点击“执行”来确认。导航通常并不复杂,因为您将使用的最常见的命令是“点击”,然后是按钮的标签。如果你不确定按钮的标签,说“显示名称”(图 9-7 )来显示每个控件的辅助功能标签。

img/486920_1_En_9_Fig7_HTML.jpg

图 9-7

语音控制显示姓名

请确保您的控件的可访问性标签准确而简短,因为这将是您的语音控制客户用来激活有问题的控件的短语。使用“显示名称”命令可以清楚地显示标签是否太长,是否遗漏了标签,或者标签是否不容易从图像中识别出来。

如果你的元素包含大量的内容或者用户不容易理解的内容,那么,一般来说,这应该作为accessibilityValue而不是accessibilityLabel提供。您还可以提供一组更短的或者用户可能会说的替代标签,通过向您的元素accessibilityUserInputLabels属性(清单 9-5 )提供一个字符串数组,确保首先给出默认标签。

playButton?.accessibilityUserInputLabels = ["Play", "Play song", "Play \(song.title)"]

Listing 9-5Providing friendly labels for Voice Control to listen for to activate a “play” control

摘要

  • 确保您的可访问用户界面是准确的,将确保开关控制和语音控制如您的客户所期望的那样工作。

  • 如果你的应用使用触摸手势,确保你提供一个不需要手势的替代方案。此外,将这些作为辅助功能操作添加到您的辅助功能用户界面中。

  • 语音控制是一个很好的工具,可以快速直观地判断你的用户界面是否合理。而且用起来很好玩。

下一章是我们对 iOS 辅助功能的总结。我们将介绍 iOS 为有听力障碍的人提供的注意事项。

十、iOS 辅助功能——听觉

如果我要给有听力障碍的人提供一条关于改进应用的建议,那就是在任何视频内容上嵌入字幕,要么通过硬编码,要么最好通过添加字幕轨道并使用AVPlayer控件来播放视频。如果你的应用大量使用音频,考虑一种替代方式来提供引人入胜的体验,也许通过利用动画。

听力设备

有些助听器设备是为 iPhone 认证而制造的,可以与客户的设备配对,以通过设备传送音频,或使用实时聆听功能从 iPhone 的麦克风直接传送到助听器。如果您检测到助听器已配对,您可能需要为您的客户提供将所有音频移动到他们佩戴助听器一侧的选项。

如果需要,您可以通过注册hearingDevicePairedEarDidChangeNotification s 或检查hearingDevicePairedEar选项集是否包含.left.right.both来确定助听器配对状态(列表 10-1 )。

import UIKit

class MyViewController: UIViewController {

    var hearingDeviceStatus: UIAccessibility.HearingDeviceEar {
        get{
            return UIAccessibility.hearingDevicePairedEar
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(hearingDevicesChanged), name: UIAccessibility.hearingDevicePairedEarDidChangeNotification, object: nil)
    }

    @objc
    func hearingDevicesChanged() {
        if hearingDeviceStatus.contains(.left) {
            // left ear device paired
        }

        if hearingDeviceStatus.contains(.right) {
            // right ear device paired
        }
        if hearingDeviceStatus.contains(.both) {
            // both ear devices paired
        }
        if hearingDeviceStatus.isEmpty {
            // no hearing devices paired
        }
    }
}

Listing 10-1Detecting changes with paired hearing devices

单声道声音

单声道音频禁用设备上所有音频的立体声。这是在系统级别完成的,不需要您的应用进行任何更改。单声道音频可以用于一侧听力损失的人,因为如果一些音频被平移到他们难以听到的一侧,立体声音频会导致他们丢失内容。例如,如果一段对话的视频被平移,使得一个人在左边,另一个人在右边,那么一侧听力丧失的人将只能听到对话的一半。

如果您依赖应用中的立体声音频,您可能想要了解此设置。例如,一个可能使用平移音频来提示敌人存在的游戏在启用此设置后将失去意义。

monoAudioStatusDidChangeNotification会告诉你该设置何时改变,isMonoAudioEnabled会返回当前状态(列表 10-2 )。

import UIKit

class MyViewController: UIViewController {

    var monoAudioStatus: Bool {
        get{
            return UIAccessibility.isMonoAudioEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(monoAudioChanged), name: UIAccessibility.monoAudioStatusDidChangeNotification, object: nil)
    }

    @objc
    func monoAudioChanged() {
      // check monoAudioStatus for current status.
      // provide alternatives if your app uses stereo audio.
    }
}

Listing 10-2Detecting Mono Audio status

字幕和字幕

可以在设备的辅助功能设置中启用音频描述。AVFoundation包含对音频描述和隐藏字幕的内置支持,因此为您的媒体使用一个AVPlayer实例将免费为您提供该功能(图 10-1)——前提是您确保您的视频中嵌入了音频描述和隐藏字幕。

img/486920_1_En_10_Fig1_HTML.jpg

图 10-1

AVPlayer 显示嵌入的字幕

如果你不和AVPlayer玩视频怎么办?也许你正在使用一个不同的控件,也许你想在你的用户界面中显示标题,或者你在你的应用中播放了一段语音音轨。在这种情况下,你需要自己显示字幕。请记住这个选项,您不仅要负责显示字幕,还需要保持字幕与音频同步。要决定你是否应该显示或隐藏字幕,你可以听isClosedCaptioningEnabled来决定是否显示字幕,closedCaptioningStatusDidChangeNotification将报告任何变化(清单 10-3) 。可以说,对于作为 UI 一部分而非内容的视频,默认显示字幕总是更好的选择。

import UIKit

class MyViewController: UIViewController {

    var captionsStatus: Bool {
        get{
            return UIAccessibility.isClosedCaptioningEnabled
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(captionsStatusChanged), name: UIAccessibility.closedCaptioningStatusDidChangeNotification, object: nil)
    }

    @objc
    func captionsStatusChanged() {
      // check captionsStatus for current status.
      // hide/show captions as requested.
    }
}

Listing 10-3Detecting changes in caption status

大多数专业视频编辑软件都允许你给视频添加字幕。理想情况下,这些应该是专业转录的,但 YouTube 提供了一个很好的免费自动生成的替代方案,作为其 YouTube Studio 软件的一部分。 1 如果你选择使用这项服务,请检查标题是否准确,并编辑 YouTube 猜错的任何内容。然后,您可以下载您生成的字幕,并将其导入您的视频编辑器。

由于 VoiceOver 会检测屏幕上的文本,因此它也可以作为一种替代方法,为没有音频描述轨道的视频添加音频描述。通过为您的视频启用字幕,在启用 VoiceOver 的情况下,您的字幕将在出现时被朗读。然而,这并不能真正取代精确的音频描述音轨。

摘要

  • 如果您发现客户启用了听力辅助功能,他们可能很难确定语音或音频的方向性。如果这些对你的应用很重要,考虑提供替代方案。

  • 使用您的视频编辑软件将字幕轨道嵌入到您的应用使用的任何视频内容中,并使用 AVPlayer 控件为您显示字幕。

既然我们已经涵盖了开发者可能需要考虑的所有 iOS 辅助功能,我们需要确保我们的更改对我们的客户产生了正确的影响。接下来,我们将考虑不同的技术来测试你的应用的可访问性。

十一、可访问性测试

这本书会给你一大堆工具和实用技巧,你可以在创建你的应用时使用,从设计、编码到整个服务。你如何在你的应用中使用这些知识,必须基于你的团队对它如何最适合你试图实现的目标的考虑。然而,除非你检查你的工作,否则这些工具没有一个能产生真实的人所需要的影响。

不要认为易访问性测试是额外的。和编码一样,你应该把易访问性作为你工作流程的一部分。将可访问性测试的考虑因素作为现有测试计划的一部分。因此,我不打算教你制定测试策略的基础知识;相反,我会提供应该成为第二天性的提示和工具。

自动化测试

有几个自动测试工具可以检查你的应用是否符合可访问的标准。将其中之一包含到您现有的持续集成套件中,将有助于确保您的可访问性标准始终保持较高水平。拥有这样一个可访问性质量关也将鼓励开发人员考虑高标准的可访问性,因为他们知道如果不这样,他们将无法发布他们的代码。

请记住,可访问性是关于你的客户如何体验你的应用,而不是复选框。自动化易访问性测试只能检查某些东西。一些商业工具已经被创建为法规遵从性工具,用于在需要时提供证据,证明您已经遵循了可访问性指南。如果你的主要目标不是因为糟糕的可访问性而被起诉,这是非常好的。我觉得防止基于可访问性的法律纠纷的更好方法是创建一个更易访问的应用。使用这些工具作为防止回归的方法,但是它们不能代替手工测试或者用户测试。

诸如此类的工具的主要好处是,您可以将它们集成到您现有的 CI/CD 管道中。通过这种方式,它们在发布前充当质量关。任何没有达到你的可访问性标准的提交都不会被发布给你的客户。

Google GTXiLib

Google 为 iOS 提供了一个开源的自动化可访问性测试工具。GTXiLib, 1 或 Google Toolbox for Accessibility,适用于 XCTest UI 测试框架,如 Google 自己的 EarlGrey。

GTXiLib 确保你的元素有一个合适的点击目标大小,有一个没有标点符号的可访问性标签,如果是按钮的话不会以“button”结尾。检查标签及其背景之间的冲突可访问性特征和对比。对比度检查只适用于标签,不适用于其他元素,比如按钮。

世界空间证明

Deque 为数字无障碍提供一系列服务,如培训和评估。他们的网站还提供了大量关于数字无障碍的博客和资源。

德克的世界空间证明 2 产品是一个用于 iOS、Android 和 Web 的可访问性测试工具包。对于移动设备,这是通过在你的应用中包含一个框架来实现的。然后,您可以将测试集成到 Android 或 iOS 上现有的单元测试套件中。实例化一个视图,然后将它传递给 Attest 函数,以便在出现可访问性问题时产生测试失败。

测试包括检查可动态调整大小的文本、按钮大小、滚动视图外显示的文本元素以及重叠控件。

XCUI 测试

iOS 的第一方 UI 测试框架 XCUI 通过检查应用的可访问性树来执行测试。这意味着任何编写良好的 XCUI 测试也是一个可访问性测试。这也意味着易访问的应用更容易编写自动化测试。

在 XCUITests 中按标签查找元素是必须的(清单 11-1 )。替代方法,比如通过可访问性标识符查找元素,虽然它们确实保证了屏幕上有一个元素,但是不能保证您已经为它设置了内容或者您的内容是正确的。如果找不到某个元素,这可能是因为您已将该元素设置为“可访问性隐藏”。同时,不要试图为了测试而让元素具有可访问性,因为这会给用户带来不好的体验。

let myButton = XCUIApplication().buttons["My Button"]

Listing 11-1Finding a button element from its label “My Button”

一旦有了一个元素,就有可能对它执行一些可访问性健全性检查。这些检查的形式可以是断言按钮是大写的,并且不以句号结尾,或者交互元素最小为 44px x 44px(清单 11-2 )。我在下面提供了一些你可以使用的示例测试(清单 11-2 和清单 11-3 )。我提供的进一步测试在一个名为 A11yUITests 3 的库中,您可以将其作为 XCUITests 的依赖项。

import XCTest

class AccessibilityTests: XCTestCase {

     let app = XCUIApplication()

     override func setUp() {
        app.launch()
     }

     func test_buttonAllButtons() {
          // Navigate to the screen you want to check

          let buttons = app.buttons.allElementsBoundByIndex

          for button in buttons {
             check(button: button)
          }
     }

     func check(button: XCUIElement) {
          XCTAssert(button.frame.size.height >= 44)
          XCTAssert(button.frame.size.width >= 44)
          XCTAssertTrue(button.label.count <= 40)
          XCTAssert(button.label.first!.isUppercase
     }
}

Listing 11-2Checking all buttons on screen have a large enough tap target

import XCTest

class AccessibilityTests: XCTestCase {

     let app = XCUIApplication()

    override func setUp() {
        app.launch()
    }

    func test_imageName() {
    // Navigate to the screen you want to check

         let images = app.images.allElementsBoundByIndex

         for image in images {

              XCTAssertFalse(image.label.contains("image"))
         }
    }
}

Listing 11-3Checking image’s

accessibility label doesn’t contain the word “image”

浓缩咖啡测试

Android 的 Espresso 测试框架包括一类可访问性检查。通过导入AccessibilityChecks来包含这些测试,并使用清单 11-4 中的代码来启用它们。

import androidx.test.espresso.contrib.AccessibilityChecks

@RunWith(AndroidJUnit4::class)
@LargeTest
class MyWelcomeWorkflowIntegrationTest {
    companion object {
        @Before @JvmStatic
        fun enableAccessibilityChecks() {
            AccessibilityChecks.enable()
        }
    }
}

Listing 11-4Enabling accessibility tests in Espresso

每当在视图上执行一个动作时,测试将自动在该视图及其子视图上执行。或者,您可以通过在enable()调用后添加setRunChecksFromRootView(true)来指示 Espresso 在根视图上运行测试套件。

AccessibilityChecks包含对许多常见可访问性问题的测试,如冗余标签、可访问性标签存在和点击目标大小。它还能够检查其他工具无法检查的可访问性问题,例如遍历顺序和图像对比度。

验证工具

可访问性验证工具是自动化测试和手动测试之间的中间地带。在某些方面,它们可以提供比自动化测试更多的细节,但是需要手工操作来运行检查。

google 扫描仪

GSCX, 4 或 Google Scanner for Accessibility,是 Google 的 GTXiLib for iOS 之上的一个库。您可以将 GSCX 包含在您的内部测试应用中。这里它在你的应用屏幕上覆盖了一个“执行扫描”按钮。按下此按钮时,GSCX 会突出显示任何违规元素。点击这些突出显示提供了失败原因列表和解释。由于 GSCX 在引擎盖下使用 GTXiLib,因此它具有相同的检查套件。

如果您正在使用 GTXiLib 进行自动化测试,这是一个友好的额外工具,可以帮助可访问的开发。

世界空间证明

如果你已经在 iOS 或 Android 上购买了 Deque 测试工具的许可证,这也带有手动模式。手动检查通过导航您的应用进行,该应用附带有 Attest companion 应用。然后,附加的应用可以对您的应用的可访问性树进行快照,并为您提供发现的任何问题的详细报告,在出现故障的屏幕截图上突出显示。

Apple 辅助功能检查器

辅助功能检查器是 Xcode 工具套件的一部分。您可以从 Xcode 菜单➤ Open Developer Tool 中的 Xcode 内启动它。然后,您可以在设备上或模拟器上以两种模式之一对您的应用运行辅助功能检查器。

Quicklook 允许您将模拟器中的用户界面元素作为目标,以查看元素的可访问性属性,如标签、特征和提示(图 11-1 )。快速查看视图的顶部会显示 VoiceOver 遇到元素时读取的字符串。按下旁边的扬声器按钮会让 VoiceOver 朗读字符串。

img/486920_1_En_11_Fig1_HTML.jpg

图 11-1

快速查看模式下的辅助功能检查器(左)显示 iOS 日历图标的辅助功能属性,高亮显示(右)

这里的大播放按钮将在屏幕上的每个可访问元素之间循环。“辅助功能检查器”会按照 VoiceOver 导航屏幕的顺序来执行此操作。“辅助功能检查器”会像 VoiceOver 一样为您朗读元素。这使得验证技术比使用 VoiceOver 本身要快得多。

审计模式对整个当前视图进行检查,并报告发现的任何可访问性问题(图 11-2 )。报告包括验证失败原因的更详细描述、位置失败的元素的屏幕截图,以及如何修复失败的提示。测试包括对比度、动态文本、点击目标大小和对大图像的描述。

img/486920_1_En_11_Fig2_HTML.jpg

图 11-2

审核模式下的辅助功能检查器报告(左)。审计创建屏幕截图,突出显示屏幕上可能存在问题的区域(右图)

辅助功能检查器还提供了使用设置选项卡切换某些辅助功能的快速访问(图 11-3 )。您可以在这里调整动态文本的大小。您还可以切换反转颜色、增加对比度、降低透明度和减少运动。

img/486920_1_En_11_Fig3_HTML.jpg

图 11-3

“辅助功能检查器设置”标签提供对某些辅助功能的控制

Xcode 环境覆盖

Xcode 为当前运行的应用提供了一个环境覆盖菜单。这在模拟器和设备上都有效。你可以在编辑器底部的调试器工具栏上找到这个按钮(图 11-4 )。在这里,您可以毫不费力地在明暗模式之间切换,并调整动态字体大小。您还可以切换几个辅助功能:增加对比度、降低透明度、粗体文本、减少运动、开/关标签、按钮形状、灰度、智能反转和无颜色区分。

img/486920_1_En_11_Fig4_HTML.jpg

图 11-4

Xcode 环境覆盖

Xcode 应用语言

Xcode 还提供了用不同语言测试应用的覆盖,包括伪语言。通过点击 Xcode 左上角的应用名称来编辑你的应用的构建方案(图 11-5 )。在运行和选项下,你会发现一个应用语言的下拉选项。在这里,您可以选择 iOS 支持的任何语言来检查您的本地化。列表底部还提供了各种伪语言选项(图 11-6 )。

img/486920_1_En_11_Fig6_HTML.jpg

图 11-6

在 Xcode 的方案编辑器中选择一种伪语言

img/486920_1_En_11_Fig5_HTML.jpg

图 11-5

在 Xcode 中编辑目标的方案

伪语言是模仿具有不同属性的语言的合成语言。这个列表中最有用的是从右向左的伪语言和双倍长度的伪语言(图 11-7 )。从右到左的伪语言模仿了你的应用在阿拉伯语中的显示方式,这种语言的阅读方向与大多数语言相反。双倍长度的伪语言复制你的字符串。这允许您检查您的布局是否足够灵活,以支持比您的开发语言占用更多空间的语言。在布局中提供足够的灵活性也有助于支持动态文本大小。

img/486920_1_En_11_Fig7_HTML.jpg

图 11-7

从右向左的伪语言(左)和双倍长度的伪语言(右)

谷歌无障碍扫描仪

谷歌的无障碍扫描仪是一个应用,可以从谷歌 Play 商店免费下载。 5 当被请求时,可访问性扫描器将对你的应用运行审计,捕捉屏幕截图并突出显示将从改进中受益的元素(图 11-8 )。该报告为您提供了失败视图的标识符,以及元素失败原因和如何修复错误的描述。

img/486920_1_En_11_Fig8_HTML.jpg

图 11-8

谷歌无障碍扫描器提出改善无障碍的建议

人工测试

虽然自动化工具和检查器在防止回归和突出不太明显的可访问性错误方面非常出色,但是存在自动化测试永远无法识别的可访问性问题。其中最常见的是确定您向客户展示的可访问性树是否合理和有意义。虽然在我们的可视化用户界面中,我们有设计师来为我们做这些,但是一旦我们将设计转化为代码,他们的辛勤工作并不总是转化为有用的可访问性树。检验这一点的唯一方法是亲自尝试。

屏幕阅读器测试

创建新屏幕或对屏幕进行重大更改时,请在启用 VoiceOver 或 TalkBack 的情况下浏览屏幕。虽然这些屏幕阅读器仅构成一种辅助技术,但由于它们都使用相同的可访问性树,因此体验应该是相似的。

一旦您熟悉了这些屏幕阅读器的工作方式,这个测试应该会成为您常规开发工作流程中快速且必要的一部分。随着你对这个测试越来越熟悉,试着启用黑屏或幕帘。这些功能会关闭显示屏,这意味着您与设备的唯一交互是通过手势和设备语音反馈。对你来说,在没有任何视觉提示的情况下关注你的应用有多简单?

需要注意什么

当使用屏幕阅读器执行手动测试时,当您导航时,有几个问题要问。

  1. 每个元素都是按照逻辑顺序呈现的吗?

    元素应该以有意义的方式相互跟随,为控件提供上下文。例如,不要在要购买的商品名称前显示“购买”按钮。

  2. 是否缺少任何元素?

    元素可能对辅助技术隐藏,但在屏幕上仍然可见。当我滑动浏览时,屏幕上的所有内容和控件都被读取了吗?

  3. 有我不希望出现的元素吗?

    如果您在屏幕上隐藏了元素,根据您隐藏它们的方式,它们可能仍然可用于辅助技术。这可能会导致您向客户提供不正确的信息,或者让您的客户进入无效状态。

  4. 浏览这个屏幕很费力吗?

    你必须经常在元素间滑动吗?元素或控件是否重复?信息是重复的吗,就像给一个按钮命名为“button”并设置一个“button”特征?也许有些元素可以隐藏起来,以便在不失去意义的情况下改进导航。考虑使用语义视图。

语音控制测试

iOS 13 的新语音控制功能首先是一个辅助功能,是为那些努力敲击屏幕的人设计的。但是它也隐藏了第二个非常有价值的功能,作为一个快速的可访问性测试工具。

通过说“嘿,Siri,启用语音控制”来启用语音控制启用后,说出“显示数字”iOS 会在每个交互元素旁边显示编号气泡(图 11-9 )。这个数字代表 VoiceOver 访问每个元素的 tab 键顺序。快速浏览这些数字将会确认您是否缺少元素或者有您不期望出现的元素。它还将帮助您验证每个元素的顺序在屏幕的上下文中是否有意义。

img/486920_1_En_11_Fig9_HTML.jpg

图 11-9

语音控制显示辅助功能选项卡顺序

接下来,说“显示姓名”iOS 会将这些编号的气泡替换为带有每个交互元素可访问性标签的命名气泡(图 11-10 )。这允许您验证标签是否存在以及是否有意义。

img/486920_1_En_11_Fig10_HTML.jpg

图 11-10

显示辅助功能标签的语音控制

用户反馈

考虑在您的应用中添加一个选项,让您的客户联系您并提供反馈。AppleVis 是一个由盲人和低视力苹果用户组成的在线社区,它建议其成员在遇到可访问性问题时联系应用开发者,以概述问题。许多发现易访问性问题的人就是这么做的。现实情况是,许多开发人员对可访问性了解不够,不知道可能存在问题。做电子邮件反馈的人往往会发现他们从中获得了好的结果。但是即使你遵循了这本书里的所有内容,你仍然不会有你的客户在使用你的应用时的体验。反馈选项是一种低成本、低工作量但有效的用户测试方式。

作为移动开发人员,我们有一个好处,尽管这可能并不总是一个好处,那就是我们以应用评论的形式收到客户的反馈。定期查看这些,并确定趋势。留意那些抱怨你的应用缺少某个功能的人。这不是你的用户没有发现这一点的错;你的特征要么不够直观,要么不够明显。

用户测试

在本书的前面,我强调了移情在软件工程中的重要性。同理心对于任何软件工匠来说都是一项非常有价值的技能。但同理心有时会把我们引向错误的道路——认为必须做一些事情,但不考虑我们选择做的事情是否有我们想要的效果。提高应用可访问性的一个原因确实是因为帮助他人让你感觉良好,但这不应该是主要原因。设计战略家 Liz Jackson 要求我们重新思考移情策略。

【同理心】具体化阶级和权力结构。你总是有移情者,然后你总是有移情者,对吗?移情者是救世主,移情者永远是接受者,这些角色永远不会改变。

【感同身受】让接受者保持沉默。你应该感激为你做的一切。

——莉兹·杰克森,移情具体化残疾污名 7

无障碍顾问和手语使用者 Marie van Driessche 告诉我们,从她的经历来看,同理心被用作不和残疾人交谈的借口。

大多数人对与残疾人交谈不感兴趣,他们更喜欢感同身受。但是同理心并没有帮助我们。

-玛丽·范·德里斯切??[8]??

我的观点是,如果你没有和残疾用户一起测试你的应用,你就没有做到可访问性。你只是在浪费你的发展时间来提升你的自我。

有许多专门讨论用户测试的书籍,它们会涵盖如何用比我更多的专业知识来做这件事,所以我不会涉及如何建立用户测试会议或实验室。但是我想表达让你的用户测试会议的参与者多样化是多么的重要。确保包括残疾人,以及来自不同背景和各种技术能力的人。最重要的是,倾听他们的反馈。可能是这样的情况,你所做的一个认为会提高可访问性的改变反而使它变得更糟。残疾人没有义务感谢你为他们考虑。相反,作为开发人员,你的职责是处理他们的反馈。

摘要

  • 和软件的其他方面一样,如果你不测试它,你怎么知道它能工作呢?

  • 最准确和最有用的易访问性测试形式是对具有不同能力的人进行用户测试。一定要倾听和相信他们的经历。

  • 用户测试可能是耗时且昂贵的,所以考虑将自动化测试作为标准发布过程的一部分。然而,您不应该过于依赖自动化测试,因为它们的范围是有限的。

十二、让您的应用具有包容性

到目前为止,我希望我已经说服了你,为什么可访问性和包容性是必不可少的。我们还介绍了每个平台的功能和原因。但是,你应该何时以及如何使用谷歌和苹果为你创造的每一个工具呢?在这一章中,我们将讨论一些你可以用来为不同的人改善你的应用体验的技术。没有特定的顺序,我们将涵盖不同的人群,并讨论一些让你的应用更具包容性的方法。我们将结合平面设计、服务设计和移动编程的元素。

最终目标是让我们的应用成为真正的包容性体验。当你通读时,你可能会注意到一些主题出现了。这不是偶然的。把你的应用定位于某一群人,这不是包容,而是排斥。通过坚持围绕设计、灵活性和尊重客户的原则,我们可以创建对每个人都更好的软件。

诵读困难、自闭症和学习困难

符合这一类别的人,以及患有其他疾病的人,如多动症、双相情感障碍等,通常被称为神经多样性。我喜欢这个词提供的框架;它改变了神经多样化的人需要克服某些东西的想法。认为自己神经多样化的人觉得他们的大脑以不同于我们其他人的方式工作。我有阅读障碍,虽然这确实给我带来了问题,即阅读和数学,但我觉得大脑处理信息的方式给我提供了独特的视角。这花了我很多年,我会一直学习新的东西,但我觉得我已经掌握了如何利用这些差异作为优势。你可以做一些事情,让包括我在内的神经多样化的人对你的应用有更愉快的体验。

设计简单

坚持合理的设计原则将对许多有学习困难的人产生重大影响。保持你的设计一致、简单、干净、整洁。坚持平台规范以增加一致性。

任何移动或动画内容都必须包含一个让您的客户暂停动画的机制,包括视频,但不包括过渡。移动内容应该只在响应用户操作时触发。

排印

不要使用太多的字体、字号或颜色。有太多的变化会让你的应用看起来混乱和不一致。但是对于一些有学习困难的人来说,太多的差异会使阅读变得更加困难。避免粗体、下划线、斜体和全部大写的文本。

对于正文,在浅色背景上使用黑色文本,而不是白色。如果可能的话,允许用户选择背景颜色是最理想的。正文应该总是自然对齐(在大多数语言中是左对齐)。如果您的设计要求文本居中或右对齐,这是可以的,但要避免完全对齐的文本,因为这会创建一堵难以解读的文本墙。

清晰的书面内容

保持书面内容简洁和描述性。明确任何行为的后果。对按钮和其他控件使用这些相同的原则。避免使用习语、首字母缩略词和缩写词,因为这些很难理解。

简明英语运动在他们的网站 1 上提供免费的建议,让你用清晰的英语写作,既能表达你的信息,又能引人入胜。海明威( www.hemingwayapp.com ,见图 12-1 )是一个伟大的免费网络应用,它会检查你的语法,并为你的文本提供美国学校阅读评分,突出需要改进的地方。

img/486920_1_En_12_Fig1_HTML.jpg

图 12-1

海明威强调我可以提高我的写作可读性的领域。老实说,这本书的大部分内容没有这一段突出红色

文本的替代

不要只展示基于文本的信息。以图像、图表或视频的形式提供冗余。对于较大的文本组,或者对于不容易转换成可视形式的文本,请提供录音。

iOS 和 Android 都提供屏幕阅读器——在 Android 上选择朗读,在 iOS 上选择朗读和朗读屏幕。注意,这些与画外音和回话是不一样的。VoiceOver 和 TalkBack 包含一个导航元素,是为有视觉障碍的用户设计的。屏幕阅读器只读文本内容。它们不读取或促进与控件的交互。您应该在启用屏幕阅读器的情况下测试您的应用,以确保它以有意义和可理解的方式阅读您的内容。您可能需要更改应用的可访问性树,以提供最佳体验。

颜色

对于一些有学习困难的人来说,特定的颜色组合会使文本显得杂乱或闪烁。避免明亮、鲜艳的颜色,尤其是在内容周围。文本使用黑色,而不是灰色或其他颜色,不管这种颜色有多接近黑色。文本内容的背景应为浅色;柔和的颜色很好。避免白色或其他亮色。理想情况下,允许客户选择适合他们的背景颜色。

准确

不要坚持书面输入的准确性。相反,提供选项。在有限的选项中,允许您的客户从微调按钮或按下按钮进行选择。在选项范围较广的地方,如文本框,提供自动完成的机会或减少键入量的建议。尽可能确保这些建议不需要完美的拼写就能出现。

对于较长的段落,允许通过听写输入文本。默认情况下,两个平台都支持听写,但如果您的应用提供了自己的键盘,则可以禁用听写,因此尽可能使用系统键盘。对任何标准文本条目启用自动更正。在您的用户提交之前,请始终让他们有机会检查和编辑他们输入的任何内容。

完全公开

用户体验专家 Harry Brignull 在 2010 年创造了一个短语“黑暗模式”,用来识别那些旨在让用户做他们不想做的事情的敌对设计模式。哈利在他的网站 darkpatterns.org 上定义了一些常见的黑暗模式。 2 这些包括但不限于,诱骗提问、确认羞辱、误导。康奈尔大学 2019 年的研究在流行的在线购物网站中共发现了 15 种不同类型的黑暗模式。研究人员发现,超过 11%的排名最高的商业网站至少有一个例子。让我们明确这一点:旨在欺骗你的用户的黑暗模式是对所有用户的蔑视,并且总是不道德的。对于有学习困难的客户来说,这些陷阱更加令人不安和困惑。

清楚执行一个行为的后果,不要在没有明确同意的情况下改变后果。例如,如果你正在收集 2FA 的电话号码,不要用它来发送垃圾短信。未知原因的意外消息的突然到来可能会令人担忧。

焦虑与心理健康

许多头条都在报道智能手机对健康的影响:“放下手机可能有助于你活得更久,” 4 无休止的观点文章,如“我如何抛弃了我的手机,打破了我的大脑, 5 以及大量的健康指南,如“你的手机影响你焦虑的 5 种方式”。看到这些标题,人们可能会很容易相信我们移动开发者创造了一个怪物。

对信息过载和新技术的恐慌比技术本身还要古老。大约在公元前 360 年,苏格拉底警告说,书面文字会“造成健忘”,读者会很难区分幻想和现实。网络成瘾或手机成瘾确实存在,而且其危害性不亚于任何其他成瘾。过度使用智能手机会导致压力、抑郁、睡眠问题、焦虑和孤独,这也是事实。88

虽然过度使用技术是心理健康不佳的一个指标,但技术使用不足也是如此。Bélanger 和他的同事在 2010 年的一项研究中发现了互联网使用和心理健康之间的“U 型关联”。他们经常被引用的研究发现,那些很少或不使用互联网的人因感到孤独而导致的抑郁程度增加了。因此,虽然我们选择的平台可能会对某些人产生上面列出的影响,但对大多数人来说,它会产生完全相反的影响。通过创造身临其境的体验,我们让人们丰富他们的生活,找到归属感。这是我们应该庆祝的事情。

确定智能手机使用的哪些领域是精神健康的风险因素几乎是不可能的。我们可以在智能手机上执行的广泛任务,加上大量使用智能手机是一种社会规范的事实,使得识别模式非常困难。 10 但是我们可以对心理健康风险因素进行一些更为一般性的研究,从而制定一些指导方针。

向导

迷失或受困的感觉可能是焦虑的催化剂。在移动领域,这种情况经常发生,迫使您的客户在我们选择的时间执行操作,例如,显示一个间隙,而不是让我们的客户在他们准备好的时候做出决定。我经常看到的另一种模式是,试图通过隐藏替代选项来让用户选择我们喜欢的结果。

你可以通过指引和路标来对抗迷失感。当客户执行任务时,向他们展示他们的进度,以重申他们已经完成的任务以及还剩下多少。 11 让你的客户按照自己的进度前进。如果可能的话,允许您的客户按照他们的顺序前进,根据需要跳过或返回到步骤。

当你的客户完成一项任务后,在他们提交之前,给他们一个机会检查他们做了什么。让他们有机会根据需要改变自己的回答。如果任何行为会对服务产生显著影响,例如破坏性行为,在允许客户做出承诺之前,以易于理解的方式解释其后果。我们的目标通常是加快我们的应用中客户的互动,但是如果你的流程可能导致重大变化或负面结果,请考虑使用积极的摩擦。积极的摩擦提供了一个停顿,让你的客户有机会再次检查他们的行动。 12

确保你的应用的外观和感觉是一致的,尽可能坚持系统控制,以帮助与平台保持一致。事情运作方式的变化,尤其是意想不到的变化,会引起不适的感觉,并增加焦虑。

通讯

可见性对于焦虑来说至关重要,与客户进行清晰及时的沟通会产生积极的效果。投资一个好的文案会给你的应用的清晰性带来显著的好处。在用户采取行动之前,就行动的后果进行简单明了的交流是至关重要的。如果您要对服务进行更改,例如重新设计,请确保提前进行沟通。 14

如果你的应用以应用内购买或订阅的形式提供付费服务,不要隐藏任何费用。让付费服务的任何选项都变得透明,并明确服务包括什么和不包括什么。允许你的客户在任何时候取消,而不需要经过重重关卡。就像在你的客户开始付费服务之前通知他们一样,确保你清楚如果他们取消的话会失去什么。暂停付费服务的选项是一个很好的补充。 十五

目标是做到这一切,同时记住不要用信息轰炸你的客户。为他们提供清晰易懂的相关信息,但要保持简短,一目了然。如果您的客户需要更多信息,请指引他们到他们可以访问的地方。理想情况下,用一个真人来提供支持。对支持可以提供什么、何时可用以及需要多长时间做出响应设定期望值。 16

自制力

许多患有焦虑症、精神障碍或学习困难的人通常会发现给自己增加限制的能力是一种积极的行为。诺和他的同事在 2019 年的一项研究中发现,不提供内容定义端点的应用,例如无限滚动,可能会导致智能手机成瘾。强制停止使用智能手机可以让我们休息一下,重新集中注意力。根据应用的用途,在应用中实现这些控件会有很大的不同。我在这里列举了一些例子来激发灵感。

允许客户管理自己的一个很好的例子来自英国挑战者银行 Monzo。Monzo 在其应用中对某些交易进行了阻止,为客户提供了一个禁止任何与在线赌博相关的交易的设置。苹果还在 iOS 中实现了一些带屏幕时间的自控功能(图 12-2 和图 12-3 )。这项功能允许我们限制我们在某些应用、类别或手机上花费的时间。当我们只希望某些基本应用可用时,我们也可以设置停机时间。

img/486920_1_En_12_Fig3_HTML.jpg

图 12-3

使用 iOS 的屏幕时间功能设置夜间停机时间

img/486920_1_En_12_Fig2_HTML.jpg

图 12-2

通过 iOS 的屏幕时间功能限制社交媒体的使用

社会的相互影响

数字服务是让人们变得社会化并找到归属感的无价工具。在你的应用中加入社交元素是志趣相投的人发现彼此的好方法,对他们的精神健康大有裨益。

但是正如我们在下面关于性别和性行为的章节中所提到的,用户之间的任何互动方式都会对你的客户和你的业务造成损害。对于用户之间的任何形式的交互,为客户提供一个阻止内容和用户的机制是非常重要的,最好是一个审核系统。

游戏化

在最好的情况下,游戏化是推动理论的数字体验分叉。推动理论是行为科学领域,研究使用积极强化来鼓励积极的行动。最常被引用的例子是在阿姆斯特丹史基浦机场的小便池中添加家蝇的图像。通过在便池的正确位置添加苍蝇,发现男性的瞄准能力显著提高,溢出量减少了 80%。游戏化可以作为一种让体验变得更积极的手段,并被证明对严重精神疾病患者有效。 19

语言学习应用 Duolingo(图 12-4 )利用游戏化成为 app store 上下载量和使用量最大的学习应用。它通过结合小的、可实现的、渐进的目标、可视化的进展以及返回并保持参与的提示来做到这一点。 20

img/486920_1_En_12_Fig4_HTML.jpg

图 12-4

多林哥的成就

虽然我们当然可以批评当你没有给予足够的关注时,Duolingo 发送的越来越需要、令人焦虑的推送通知;在很大程度上,Duolingo 是一个游戏化做得很好的例子。它丰富多彩,友好,有趣,重要的是不会迫使用户付费。

游戏化往往是心理操纵的委婉说法,让你的用户做一些他们本来不会做的事情,比如花钱、执行任务、分享个人数据或许多其他例子中的一个。这两个应用商店都充满了“免费增值”游戏,糖果粉碎和部落冲突就是著名的例子。许多这样的游戏,虽然看起来是免费的,但需要大量的真钱投资才能取得重大进展。

英国移动网络 GiffGaff 已经取代了他们的一些核心业务运营,如客户支持和销售,或者 GiffGaff 称之为“社区成员”。每完成一项任务都会获得积分,比如在应用中回复一位客户的支持查询。GiffGaff 以较低的价格为每个人奖励社区成员。获得的任何积分都可以兑换成客户账单上的积分。为积极的客户行为提供奖励是提高客户忠诚度的一个好方法,可以鼓励你的应用需要的实践,让你的体验更愉快。但对我来说,将核心业务职能外包给你的客户跨越了道德劳动边界。

在用游戏化吸引你的客户和播种坏习惯之间,这是一条微妙但重要的线。诺和他的同事认为,在线与朋友竞争的游戏化可能是智能手机成瘾的一个原因。 21 诺伊的团队使用了 Snapchat 的 Snapstreak 功能的例子。Snapstreak 用一个火表情符号和连续记录保持的天数奖励定期发帖。鼓励用户保持这种势头。但这可能会导致用户感到发帖的压力,并可能导致高度的压力和焦虑。

经济困难

一台电脑、一台平板电脑或一部智能手机以及一个互联网连接不是一笔微不足道的财务支出,也是一笔经常性支出。对于大约 20%的互联网用户来说,智能手机是他们访问互联网的唯一途径。仅使用智能手机的互联网用户更有可能生活在贫困地区。40%的人说他们使用智能手机的原因是因为其他选择的成本。

然后,作为移动开发者,我们有责任确保我们保持与桌面体验同等的功能。围绕我们支持的设备,也需要达成微妙的平衡。放弃对旧设备和操作系统的支持可以给我们带来好处,比如降低复杂性和令人兴奋的新功能。但我们可能会离开没有应用的用户通常是那些最弱势的用户,他们可能很难升级。

还要考虑到残疾人通常比正常人有更大的成本负担——英国残疾人慈善机构 Scope 估计每月要多支付 750 美元。 23 残疾人也更容易失业。美国的就业率为 3.7%;在残疾人中,这一比例上升至 8%。 24 因此,残疾人更容易陷入经济困难的境地。

数字素养

你是专家。虽然我们在软件工程方面都有自己的专长,但你仍然是专家。对许多人来说,任何数码产品都是一个谜,也许是因为他们缺乏经济来源。也许他们成长在计算机还没有普及的时代,没有找到学习的理由。作为一个经常性的、长期的数字公民,很容易忘记或看不起许多不使用互联网的人,甚至可能是害怕互联网的人。但对网上银行的担忧是真实的,80%的人都有这种担忧。 25

基本数字技能

英国政府与亚马逊(Amazon)和微软(Microsoft)等集团协商,编制了一份基本数字技能清单。政府认为这些技能是任何人能够胜任使用互联网的基准。

当你在读这本书的时候,我想你可以掌握所有这些重要的数字技能。事实上,它们中的许多看起来是如此的基本,以至于几乎不值得一提。然而,对英国的许多人来说,现实是他们不能做这些我们认为显而易见的事情。21%的英国成年人口,1130 万人,不具备这些技能。更令人担忧的是,8%的英国成年人口,430 万人,不具备这些技能中的任何一项——一项都没有。我总结了一些关键技能和一些与移动最相关的技能。

交流

沟通、协作和共享信息所需的技能,包括以下技能

  • 我可以在 WhatsApp 或 Messenger 等消息平台上建立一个群组,与朋友或家人聊天。

  • 我可以在社交媒体上适当发帖,并访问和发帖到 Mumsnet 或 Reddit 等论坛。

  • 我可以将照片和其他文件作为电子邮件附件发送给朋友和家人。

  • 我理解安全交流的重要性。

处理信息和内容

安全地查找、管理和存储数字信息和内容所需的技能包括

  • 我明白并非我看到的所有在线信息和内容都是可靠的。

  • 我可以使用搜索引擎查找信息,并利用搜索词产生更好的结果。

  • 我知道云是我可以在远程位置存储信息和内容的一种方式。

  • 我可以从 Spotify 或 Apple Music 等合法网站上下载音乐,或者从网飞或亚马逊 Prime 等合法网站上观看流媒体电影。

交易

注册和申请服务、购买和销售商品和服务以及管理在线交易需要以下技能:

  • 我可以为公共服务建立在线账户。

  • 我可以为政府服务和零售商建立在线账户,以便通过亚马逊或易贝等在线订购和支付商品。

  • 我可以使用旅游网站和应用来订票。

  • 我可以安全地管理我的在线资金和交易。

解决问题

使用数字工具和在线服务寻找问题解决方案所需的技能:

  • 我可以使用在线教程、常见问题解答和建议论坛来解决问题,并提高我使用设备、软件和应用的技能。

  • 我可以通过使用像 YouTube 上的教程视频来找到如何做某事。

  • 我可以使用互联网查找与需要执行的生活任务相关的特定信息,例如,查找食谱或帮助计划旅行的信息。

在线安全合法

在生活和工作中保持在线安全、合法和自信所需的技能包括

  • 我对网站和帐户使用不同的安全密码来保护我用来访问我的在线帐户的信息,并确保我不会与任何人共享这些信息。

  • 我可以识别电子邮件、网站、社交媒体消息和弹出窗口中的可疑链接,并且知道单击这些链接或下载不熟悉的附件可能会使我和我的计算机面临风险。

  • 我了解开展在线活动的风险和威胁,以及安全工作的重要性。

  • 我理解保持我的计算机系统和安全软件最新的重要性,并且我允许它们在提示时更新。

调查的结果

在上述五个领域中,技能最高的领域是处理信息和内容。91%的人口拥有这方面的足够技能。然而,21%的受访者无法下载或保存他们在网上找到的照片。

尽管我们与数字化组织的互动越来越多,但在 2015 年至 2018 年期间,这些技能仅增长了 1-2%左右。只有 87%的人能够完成在线申请表,85%的人能够在线购买商品或服务。我们在移动领域特别感兴趣的是,只有 76%的人能够在他们的设备上安装应用。

你≠你的用户

在过去的 25 年里,我们主要是为设计[软件]的人设计[软件]。

—vasilis van gemert,独家设计【28】

作为以这种或那种方式靠电脑谋生的人,我们很容易忘记,在知道如何使用我们创造的产品时,我们处于一种特殊的地位。现实中,我们的客户和我们不一样;他们甚至不太可能像我们的朋友。

我相信我们都能想到一个不使用技术的家庭成员。如果他们今天需要拿起设备来完成任务,他们会知道煎锅图标意味着他们可以搜索吗?他们会在意你给事物起的可爱名字吗,比如当你指的是存储时的“云”吗?我记得在苹果经销商工作的一段时间。一位顾客认为 AirPort 是苹果 Wi-Fi 的品牌名称,这意味着他们只能在飞机上使用 Wi-Fi。

我们不能在那里握住客户的手。我认为我们或我们的客户也不希望这样。思考我们的客户有能力做什么和屈尊俯就之间有一条细微的界限。避免行话,无论是书面的还是视觉的,都是很好的第一步。为了在其他方面取得成功,我们需要更多地了解我们的用户。

用户测试

开发任何产品时,了解市场和你的客户群是很重要的。当我们在创建软件时,这有什么不同呢?作为开发人员,我们没有理由不能与用户交流。除此之外,我们中的许多人更喜欢尽可能少的人际交往。

用户测试既耗时又昂贵。但对于任何规模的企业来说,这可能是成功产品和失败产品的区别。产品经理可能对识别产品及其市场的用户测试感兴趣。但是作为开发者,我们可以使用用户测试来识别我们市场的技能。这正是重要的数字技能发挥作用的地方。

英国政府开发了前面列出的基本数字技能作为用户测试工具。在邀请人们体验他们的软件之后,在动手之前,他们向参与者提问,以确定他们的数字技能。然后,参与者被标绘在一个标尺上(图 12-5 ),从 0 开始,从来没有也永远不会使用互联网。移动到 4,这意味着参与者可以做到上述基本数字技能之一。7 表示参与者可以执行所有必要的数字技能。9 点结束,你和我会坐在那里,这意味着我们是以数字为生的专家。

img/486920_1_En_12_Fig5_HTML.jpg

图 12-5

英国政府的数字包容性量表

您可以通过两种方式使用该秤。如果你真的有信心准确了解你的应用的用户群,作为研究参与者,你可以使用这个量表来了解你的参与者的技能。规模将帮助您了解如何先进和冒险,你可以使您的功能。只要相信你的用户会追随你就行了。

或者,你可以使用这种尺度来确保你的应用简单易懂。检查一下,对于那些数字技能较低的人,他们可以通过你的应用引导自己,而无需你光顾他们。

年龄

科技有代沟。99%的 11-18 岁青少年可以上网;其中 96%的人每天都使用互联网。60 岁以上的人有 28%根本不上网。 29 这是一代人之间的巨大差距。我们中的许多人是伴随着无处不在的计算成长起来的。其他人可能从未听说过计算机,或者知道计算机是一种他们永远不会使用的晦涩的工业工具。如果这突然成为我们日常生活的一部分,我们当中没有在农场长大的人会有信心,甚至喜欢在 30 多岁时学习复杂的农业机械的复杂细节?在前面关于数字技能的章节中,我们在更广泛的背景下讨论了许多问题。

虽然这种技能差距正在逐渐缩小,但我们无法回避的是,世界人口正在老龄化,随着年龄的增长,我们的身体也会发生变化。随着预期寿命的增长,众所周知,我们中的大多数人将会看到我们的身体开始让我们失望。因此,尽管你现在可能不会使用辅助技术,但在未来,当你的眼睛、认知、运动能力或任何其他身体方面开始衰竭时,你会开始欣赏它们的存在。这对你的客户也是一样的。

文本大小是通向可访问性的大门

age 的“网关”辅助功能是改变文本大小的能力。在 iOS 上支持动态类型并在 Android 上使用可扩展的磅值是任何应用中可访问性的必备第一步。可调整的文本大小通常是客户可能使用的第一个辅助功能,通常不认为它是一个辅助功能。

可定制的文本大小介于我们可能认为的辅助功能和定制之间。因此,支持这些有助于规范辅助功能的使用。它还可以教育你的用户,在他们的设备上也可以考虑可访问性。

没有具体的考虑使老年人比一般人受益。但是老年人更有可能从无障碍功能中受益。确保你已经遵循了本书其他地方的指导方针,将意味着你的应用为老年用户提供了更好的体验。

性别与性

记得高中的时候学过基本数据类型。这个练习是为了创建一个数据捕获表单,供人们注册一个聚会。尽管要求在 Excel 电子表格中注册聚会的人不太可能有很多客人。我们将年龄存储为整数,将姓名存储为字符串,将性别存储为布尔值。从工程的角度来看,以这种方式存储性别从来都不是使用布尔值的好例子。

我们通常不应该将任何真实世界的值表示为布尔值。生活很少是二元的选择或决定。将自己限制在两个选项之内会限制未来重构的范围。虽然我见过用枚举来表示一个人的生活状态,但这是一个极端的例子。虽然这意味着一旦冬天不可避免地来临,重构夜王的死亡之军就不再是一个项目了。对于除了生与死之外的任何事情,我会推荐一个 enum,允许未来的变化。

其次,对于任何涉及身份的事情,只允许两种选择意味着对使用你的应用的人做出假设。无论你是主动还是被动做出这个选择,都是如此。这种对你的客户的假设可能是不正确的,你的许多用户不会因此而感谢你。

性别

性别是一个很好的例子,说明为什么使用布尔值来表示真实世界的值是一个坏主意。性别不是,也从来不是一个二元选择,但就在不久前,作为一个社会,我们认为它是这样的。因为这种社会规范,我们创造了许多软件来接受男性或女性的二元选择。

不管你个人的观点如何,有一个压倒一切的技术软件工程师的观点告诉我们这是一个糟糕的选择。通过将所有这些系统编程为使用不灵活的布尔值,当我们不可避免地改变我们系统的工作方式时,我们产生了大量的技术债务。在性别认同的例子中,社会要求我们改变系统的运作方式。这种变化现在需要后端数据库迁移和前端重新设计,以允许这种扩展的选择。

这里软件工匠的观点是,通过限制只有两个选择,我们排除了那些不区分性别的人。我们增加了他们被排斥的感觉,为他们参与社会制造了障碍。许多性别非二胎的人在青少年或年轻成年时被他们的家庭排斥,因此,在开始生活时就认为他们没有归属感。多达 48%的非未成年人承认试图自杀。允许人们在生活中向我们的软件表明自己的身份不仅仅是数据类型之间的决定。这是一个明确的信号,每个人都欢迎做真实的自己,不管是谁,他们都是我们社会的重要成员。这并不能阻止你的用户选择自己的性别。

存储性别

作为一个工程问题,在维护数据有效性的同时,一个解决方案是将性别存储为一个枚举。作为最基本的要求,我建议男性、女性和非二胎。

一个更具包容性的解决方案是将这些选项和更多选项作为自动完成建议显示在文本字段中。因此,我们允许人们输入他们自己的选项。

不要提供“其他”的第三个选项,或者在“其他”选项后面隐藏选项。“其他”是一个明确的信号,表明你认为不认同男性或女性的人不那么重要。当然,确保你的系统有一个选项来改变性别选项。

第三,也是更可取的选择,是考虑你为什么要捕捉性别。除非你的应用涵盖健康,否则我很难想出一个真正的理由来要求获取这些数据。

重要的是,记住性别≠代词。

代词

不要假设他/他和她/她的代词分别代表男性和女性。如果你遵循了我上面的建议,无论如何你都不会做出这样的假设。代词最好的选择可能是询问你是否需要它们。但是如果你的应用用代词指代你的客户,确保你提供的选项和性别选项是分开的。提供“他们/他们”选项,并允许您的客户根据需要更改他们的代词。

标题

当你的顾客认同时,提及他们是对他们最基本的尊重。这当然也适用于名称和代词,但对于标题,也有其他选择。

就我个人而言,我不喜欢被称为先生,更喜欢被直呼其名。许多人认为使用头衔是礼貌的,尤其是在你和你所称呼的人不友好的情况下。许多人理所当然地获得了博士、先生、上尉等头衔。,不允许这些选项可能是不尊重。不幸的是,由于产生这些头衔的现在有些过时的阶级制度,许多头衔是有性别区分的。对于那些没有性别区分的,确保不要假设性别。包括一个非性别的 Mx,也可能没有标题的选项。

首选名称

软件的一个常见要求是存储一个合法的或出生时的名字。例如,我们预计在人力资源、银行或政府服务的软件中需要“真实”的名字。但是对于我们来说,想要以不同的方式为人所知并不罕见。我喜欢别人叫我罗伯,但我的护照上写的是罗伯特。这可能会给我在通过工作预订国际旅行时带来问题,因为系统会自动以 Rob 的名义预订所有东西。

对我来说,能够将我的名字和我喜欢的名字分开,只是一种便利。我既不用让同事叫我罗伯,也不用修改旅行预订来匹配我的护照。但对许多人来说,这是他们身份的重要组成部分。对于任何已经或正在改变身份的人来说,被一个他们不再认同的名字所知会令人非常不安。

代词项目的 Chelsea Hostetter 观察到“实名”政策会增加歧视,特别是在某人可能倾向于一个群体而不是另一个群体的情况下。脸书对他们认为没有使用“真实”姓名的账户进行标记的政策会让那些被标记的人感觉像是骗子。

允许你的客户用他们觉得舒服的名字来称呼,并允许他们在需要的时候改变这个名字。可能有些应用需要出生或法定姓名,但只在必要时收集和使用这些信息。

骚扰

这一部分同样适用于社会中的任何少数群体,实际上也适用于本书中的任何类别。我选择把它放在这里,因为性别和性,不幸的是,提供了技术如何使骚扰成为可能的最鲜明的例子。

同性恋交友应用 Grindr 被专制政权用来识别,在某些情况下,追捕 LGBTQ+社区的成员。 33 在英国,Grindr 等 app 被连环杀手斯蒂芬·波特用来引诱他的受害者。 34

现实是严峻而简单的。如果你的应用为你的用户提供了任何与他人互动的方式,你的应用可以,也将会被用于骚扰,不管你的应用的主要目的是什么。有这样的例子,像 Square 35 和 PayPal 36 这样的金融应用被用来通过发送小额资金和短信来骚扰人们。在任何可能进行个人交互的地方,阻塞机制都是必不可少的。你可能还需要一个审核和报告系统。所有这些都意味着我们需要彻底考虑在我们的应用中增加用户交互的决定及其后果。

耳聋和听力损伤

智能手机主要是一种视觉媒介。因此,在开发应用时,很容易忘记考虑听力障碍。尤其是如果你的应用不使用音频,你可能会认为听力障碍根本不是你要考虑的问题。但是这里有一些事情需要考虑。

清除内容

语言是复杂的,在人脑中是相互联系的。我们没有,也可能永远不会完全意识到我们对语言的使用和理解是如何发展的。这就是说,耳聋的人可以通过其他方式进行补偿,例如,高标准的文化水平,这可能是一种常见的误解。由于几个原因,情况往往不是这样。失去听力语言的语境会阻碍学习读写技能。还要记住,对于喜欢手语的聋人来说,口语或书面语永远是第二语言。因此,你应该总是喜欢清晰,简洁的语言,有逻辑的布局,没有成语和谚语。

字幕

如果你的应用以任何方式使用音频,你应该总是以一种形式提供标题。你可以以最简单的方式提供字幕作为单独可用的抄本。理想情况下,在你的视频中嵌入隐藏字幕,就像我们在前面章节中提到的那样。

请记住,这同样适用于音频内容,而不仅仅是视频。举例来说,如果你的应用包括语音旁白,比如一个游戏,你也应该给这些加上标题。此外,最适合游戏,不要依赖您的客户来确定音频发出的方向。如果你的游戏向左移动声音来暗示左边有坏人,也要提供视觉提示。

身体和运动技能

正如我们所提到的每一种损伤一样,身体和运动损伤涵盖了各种各样的能力。这包括从丢失一个手指从而使多点触摸手势变得更难,到只有最小的移动。在晚年,斯蒂芬·霍金只能通过一块脸颊肌肉活动,但却能操作电脑,这是出了名的。像任何用户一样,您的身体有障碍的客户会喜欢您的用户界面的简单性。

精确

不要坚持你的客户的准确性;所有交互元素在每个方向的最小尺寸都应该是 44 像素。谷歌建议 Android 上的交互元素至少应为 48 像素。重要的是,记住也要分开交互元素,这样可以防止意外输入。

键盘和开关访问

测试您的应用是否适合每个平台的交换机访问功能。有两个常见的陷阱需要注意。

不幸的是,网络上的一个普遍现象是“键盘陷阱”使用 tab 键导航时会出现键盘陷阱。可以导航到某个项目,但不能再次导航离开。我还没有在移动平台上常见到这种情况,但它确实发生了。在手机上,这也可能发生在开关控制和屏幕阅读器上。

第二,保证元素的逻辑导航顺序。当您在没有启用切换控制的情况下通读页面上的元素时,您是按照切换控制功能的顺序通读的吗?可访问性差的应用会导致键盘高亮在屏幕上跳来跳去。这种随机的运动充其量是令人沮丧的。

这两个平台都可以通过外部键盘输入进行导航,无需客户进行任何激活。然而,iOS 确实需要一些标准功能的帮助,比如分页。开源项目 KeyboardKit 38 应该对此有所帮助。在可能的情况下,也实现键盘快捷键。

超时设定

有运动障碍的人可能会因为许多原因而难以与他们的设备进行交互。双手稳定地使用智能手机几乎总是比任何形式的灵活性下降的人都快,无论是手指或四肢缺失,帕金森氏症等导致颤抖的情况,还是使用开关或语音控制的严重受限时刻的人。要求客户在特定时间内采取行动将给有汽车问题的客户带来巨大压力。

取消任何不必要的时间限制。例如,当您需要为客户的安全设置时间限制或防止拒绝服务时,请确保在维护安全性的同时尽可能延长时间限制。如果时间限制即将到期,保存进度并通知您的客户时间限制即将结束。向他们提供延长或更新时间限制的选项,并从他们停止的地方开始。

快捷指令

尽可能提供快捷方式。快捷方式可以绕过应用的公共区域,或者是填写表单的更直接的方式。举个例子,如果你的应用处理电子商务,你需要每个订单的送货和账单地址。让你的客户为他们的每个订单完整地填写这两个地址是失去任何回头客的最好方法。但是对于那些觉得与你的应用交互是一件苦差事的人来说,这不仅仅是一个小挫折。此处的解决方案包括让您的客户复制帐单和送货地址,使用邮政编码或邮政编码查找工具,以及允许回头客保存地址以备后用。

自动化

对于身体有缺陷的人来说,任何运动都是昂贵的。根据个人的障碍,这可能包括从穿过房间到打开电灯开关,再到在智能手机屏幕上导航。因此,任何减少运动需求的东西都是可取的。自动化是一个非常好的工具。除了自动化智能家居之外,我们还可以利用自动化让我们的应用内和应用间的日常任务无需触摸。

在 Android 中,谷歌提供了应用操作。应用操作允许特定类别的应用连接到谷歌助手,这意味着只需一个语音命令就可以快速访问日常活动。

对于 iOS,苹果通过 Siri 快捷方式为此提供了一个梦幻般的框架。Siri 的意图是让你的客户不仅可以通过单一的语音命令控制你的应用,还可以通过快捷方式应用将活动链接在一起。然后,客户可以通过一次点击、语音命令甚至外部触发(如时间、位置或点击 NFC 标签)来触发快捷方式。

视觉障碍

可调整的文本大小和屏幕阅读器支持对于包含有视觉障碍的人是必不可少的。支持客户选择的文本大小是任何移动应用的重中之重。如果你没有听这个设置,或者如果你正在以较大的尺寸截断或重叠文本,你需要重新考虑你的一些开发过程。假设你已经支持这一点,其他声音设计原则将有助于包括那些有任何类型的视觉障碍。

小心使用颜色

颜色是一种快速传达意义的强大方式,但不是每个人都以同样的方式体验它。有些人对颜色的感知有缺陷,很难区分。一些视力模糊或受损的人会发现相近的颜色会相互融合。在应用中选择何时、何地、如何以及使用什么颜色是至关重要的,你选择的颜色要足够不同,以便于确定。万维网联盟的网页内容可访问性指南(第三章)建议文本与其背景的对比度应为 4.5:1,或者对于 18pt 左右或更大的大文本为 3:1。为了更好地符合 WCAG,目标是 7:1 的对比度。

记住不要仅仅依靠颜色来传达意思。虽然这是展示地位的好方法,但绿色或红色的高光并不是每个人都能看到的。还要记住,不同的颜色在不同的文化中有不同的含义。总是将颜色与形状或文本结合起来,这样你就有了一种冗余的方式来呈现信息。记得给对讲和画外音加个有意义的标签。

有许多检查对比度的在线工具,但我推荐从 Paciello Group 下载免费的彩色对比度分析器(CCA)(图 12-6 )应用。 39 CCA 允许您以多种格式输入颜色,包括十六进制和 RGB,或者您可以使用吸管工具选择一种颜色,并使用滑块调整它们。然后,CCA 将向您展示一个您选择的文本和图标颜色组合的示例,并指出您的组合符合哪个 WCAG 规则。然而,检查你与真人的对比是至关重要的,因为即使是一些及格的组合对一些人来说也不够高。此外,文本越小,所需的比率越高。

img/486920_1_En_12_Fig6_HTML.jpg

图 12-6

Paciello 集团的免费色彩对比分析软件

逻辑布局

为你的应用设计一个好的 UX 会给有视觉障碍的人带来很大的不同——以流畅、逻辑、线性的布局呈现内容。目标是保持内容以自然的方向呈现——对于大多数语言来说是从左到右。缩放或放大用户将从自然阅读侧导航您的应用,这意味着他们可能会错过任何从右侧呈现的内容。如果您在文本旁边显示状态图标,请考虑这一点。如果你的图标在文本的右边,放大用户可能永远不会意识到这个状态指示器的存在。

使用我们在 Android 的第四章和 iOS 的第六章中讨论的语义视图技术将相关控件链接在一起。链接元素将有助于增加控件的上下文,同时使导航更加直观。

种族和国籍

种族是一个很好的例子,说明了为什么在我们的发展过程中让不同背景的人参与进来是很重要的。确保您的团队包括来自不同国家和文化的优秀工程师。这样,你会更多地了解你对使用你的应用的人的无意识假设。

机器学习

不幸的是,机器学习为我们提供了一些缺乏多样化思维可能适得其反的鲜明例子。机器学习是另一种适合我们在本章讨论的任何类别的技术。但是种族显然是一个领域,机器学习已经被证明是麻烦的。

2017 年,人工智能自拍应用 FaceApp 因使用未经检查的刻板印象而陷入了几次争议。他们提供的一个过滤器改变了你的脸,让你看起来像一个不同的种族。虽然我们大多数人可能会质疑这种形式的数字黑脸是不是一个好主意,但直到用户告诉他们之后,FaceApp 才知道。

在更早的一次事件中,FaceApp 取消了一个旨在让其用户看起来更有吸引力的过滤器。所谓的热度过滤器是一种机器学习模型,可以对你的脸进行变换。该模型将这些转变建立在它对被告知“性感”的人的了解上。这导致模型使人们的肤色变亮。FaceApp 首席执行官雅罗斯拉夫·冈查罗夫告诉 TechCrunch:

这是由训练集偏差引起的底层神经网络的不幸副作用,而不是有意的行为。

—FaceApp CEO 雅罗斯拉夫·冈查罗夫,TechCrunch 41

机器学习模型确实是一个令人担忧的原因。但这不能怪模特。尽管大肆宣传,机器学习和任何其他计算机系统一样,是我们输入的产物。因此,机器学习模型的一个重要功能是揭示我们的偏见。在 FaceApp 的案例中,训练模特的小组根据欧洲的理想选择了美女。

虽然 FaceApp 的例子引起了苦恼和冒犯,但有些例子更进一步。美国法院使用的机器学习系统通常会建议对黑人罪犯判处比白人罪犯更长的刑期。 42 机器学习模型的黑箱性质加剧了这个问题。测试或审计为什么一个模型会做出它所拥有的决定是不可能的。

机器学习也是一项令人难以置信的技术,它帮助推动了移动应用中许多最令人兴奋的新功能。如果没有它,我们在 FaceApp、Snapchat 和 Instagram 中看到的图像处理过滤器就不可能实现,自然语言处理也不可能实现语音控制和语音访问等辅助功能。如果你选择在你的应用中使用机器学习模型,请仔细考虑应用。对你的数据有信心,并在开始之前尝试理解你的数据的偏差。尽可能多地测试,以发现你的模型在哪里检测到了你隐含的、无意识的偏见。你的模型甚至可以帮助你改进你的数据收集。

本地化

本地化是一个巨大的话题。我们在前面的章节中介绍了本地化语言应用的基础知识。但是本地化是一本新书的主题。除了翻译文本,记住从右到左语言要求你考虑应用的布局,包括 UX 设计和如何在应用中构建设计。使用流动布局来适应不同的元素大小会有所帮助。请记住,不同地区的格式是不同的。生活在英国,我经常在预订去美国的旅行时感到困惑,不得不反复检查我是否输入了正确的日期月份和日期顺序。如果预订应用检测到我的位置,这将减少我的预订体验的压力。

即使你的应用只在单一市场可用,我们生活在一个全球化的社会中。2017 年,美国人口普查局发现,近 22%的美国人在家里说英语以外的语言。大约有 7000 万美国人可能会发现,如果你的应用有另一种语言可供他们使用,使用起来就不会那么复杂。

发展无障碍和包容性

我真诚地感谢你选择拿起我的书,不仅因为这本书对我个人意义重大,还因为当人们想了解更多关于移动可访问性和包容性的信息时,我总是很兴奋。作为塑造现代世界运作和进步方式的人,我们拥有特殊的地位。我们有其他领域的同事没有的机会。如果一座只有台阶的建筑发现人们无法进入,更换台阶是一项大工程。在软件中,我们可以快速识别和修复类似的问题。然后,我们可以在应用审查所需的时间内将改进后的软件交到客户手中。

希望有了这些新知识,你可以开始识别应用中潜在的可访问性问题。当你获得更多的知识时,请在你获得自信时把它传递下去。安排研讨会,或者在午餐和学习会议上发言。对于较大的团队,考虑开始一个实践项目的可访问性社区。分享潜在问题和解决方案只会为您的客户带来更好的结果。

从我的经验来看,我们的大多数开发伙伴都对可访问性感兴趣,并希望做正确的事情。他们有时只是缺乏如何做到这一点的知识。

A11y 社区

就像整个 iOS 和 Android 社区一样,有一个充满活力和专注的数字无障碍社区。这个社区一般使用缩写 A11y。我们从 accessibility 中取出 A 和 Y,11 指的是中间的 11 个字母。这也为 ally 创造了一个令人愉快的谐音。这个首字母缩略词也有助于区分热衷于无障碍技术的人和使用无障碍功能的人。如果你想了解更多关于无障碍技术社区的信息,试着在 Twitter 或 LinkedIn 上搜索#A11y。我有一个 Twitter 账号@MobileA11y,用来分享与移动相关的可访问性内容。

聚会和会议

A11y 社区举办了一系列讨论最佳实践和进步的会议。当你遇到一个特别棘手的可访问性 bug 时,这些地方是扩展你的知识和会见专家询问更多信息的好地方。我还在一次活动中获得了学习英国手语的机会。

世界各地有许多关于无障碍的聚会和会议,但我参加过,并强烈推荐英国的无障碍伦敦 44 和无障碍诺丁汉 45 。虽然我没有机会参加,但移动辅助功能专家 Paul J. Adam 是德克萨斯州奥斯汀市奥斯汀辅助功能和包容性设计会议的组织者。移动无障碍专家 Jon Gibbins 是英国布里斯托尔布里斯托尔包容性设计和开发会议的组织者。

在英国爱丁堡,有一个为期一天的无障碍会议,会上有一些发人深省的演讲。你可以在他们的博客上找到这些演讲的视频、文字记录和幻灯片。 49

posted @   绝不原创的飞龙  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示