iOS上的VoiceOver:解决常见问题

当您承诺为盲人调整应用程序时,经常会出问题:顺序会丢失,然后焦点会放在错误的位置。另一方面,有UX,很容易错过,因为您不知道可能的问题。在本文中,我们将介绍典型的问题及其解决方案。



改编iOS应用程序是一个很大的话题,所有内容都不适合一篇文章,因此我分系列发布了它们。

  1. 语音控制和VoiceOver:如何使应用程序适应盲人或静止者
  2. iOS上的VoiceOver:每个控件的行为都不同。
  3. iOS上的VoiceOver:解决常见问题。
  4. VoiceOver,语音控制和UI测试的实现之间的区别。(进行中)

第一部分中,我们使用VoiceOver处理盲人应用程序的适应问题:签名控件,对其进行分组,固定导航。第二部分中,我们进一步研究了控件所具有的“功能”,以改善控件对盲人的工作并总体上提高应用程序的可用性。

今天,我们将继续努力调整披萨屏幕:我们将更改抓取订单,汇总购买信息,修复模式窗口并改善加载指标。

控制重新排序


如果您没有为VoiceOver设置正确的传递控件顺序,则很可能会按照您希望的错误顺序绕过和读出元素。例如,这发生在按钮“关闭”和“到篮子”上。

屏幕将滚动并且按钮在上方UIScrollView事实证明,VoiceOver首先尝试绕过内部的所有元素UIScrollView,然后才找到顶部的按钮。对于用户而言,VoiceOver的这种行为将是错误的:按钮位于顶部,因此迭代和评分应从它们开始。

首先,让我们首先弄清楚VoiceOver如何确定控件的顺序。他这样做的方式是:从property中获取元素accessibilityElements默认情况下,view它们都在那里isAccessibilityElement = true

现在,我们可以通过覆盖将按钮放在开头accessibilityElements

override var accessibilityElements: [Any]? {
    get {
        var elements = [Any]()
            
        elements.append(contentsOf: [closeButton, cartButton])
        elements.append(contentsOf: contentScrollView.accessibilityElements)
            
        return elements
    }
    set { }
}

通过shouldGroupAccessibilityChildren分组


通常,VoiceOver尝试以自然的顺序读取元素-从左到右,从上到下:



如果您对控件进行了分组,则需要VoiceOver跳到分组中最接近的元素,而不是按读取顺序。设置.shouldGroupAccessibilityChildren = true,顺序将开始考虑元素的接近度。该属性必须设置view为所有元素的父级



将第一项指向焦点


另一个阅读顺序问题是,当VoiceOver首次打开屏幕时,它会选择左上方的元素。通常,这是后退按钮。一方面,这样可以使您在输入错误时快速返回上一屏幕。另一方面,这是我们失去对所处屏幕的理解的方式。如果将焦点手动设置为所需的控件,则可以纠正这种情况。

苹果做什么?将焦点放在“返回”按钮上
很奇怪UINavigationController,我可以将其放在打开的屏幕标题上。从便利性的角度来看,在我看来,专注于标题或第一个控件是对的,因此我们提供了有关新屏幕的更多信息。您可以用擦洗手势返回

您可以使用警报功能重新排列焦点UIAccessibility.post(notification: …)它带有两个参数:

  • 物种之一UIAccessibility.Notification
  • 警报应应用于的对象。通常,这是一行包含文本或对象的行,必须在通知后选择。

您可以将焦点放在viewDidLoad中的标题上:

override func viewDidLoad() {
    super.viewDidLoad()
        
    UIAccessibility.post(notification: .screenChanged,
                         argument: titleLabel);
}

您可以转移要聚焦的对象或要发音的文本。

对象警报的类型


  • .screenChanged — , . - .
  • .layoutChanged — . , , «» .
  • .announcement — . . , . , .

    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
    		UIAccessibility.post(notification: .announcement,
                             argument: text)
    }
  • .pageScrolled.UIScrollView « 3 5», . , , .
  • .pauseAssistiveTechnology .resumeAssistiveTechnology —  VoiceOver.

原生显示模式窗口


与VoiceOver一起使用时,可以(并且将)拍摄开发中允许的所有门框。例如,我们创建了一个消息提要,以使消息从底部开始,我们决定先翻转UITableView然后翻转所有单元格。外观上一切正常,但列表将在VoiceOver中用三根手指上下滚动。

我们还面临一个问题,我们无法以任何方式更改成分,因为不可能将焦点放在窗口上。发生这种情况是因为我们展示了该视图,而没有UIViewController使用特殊的UIPresentationController.VoiceOver来解决.firstResponder,而我们view没有。



如果没有时间重写,则可以view将属性设置为accessibilityViewIsModal然后,VoiceOver将仅专注于此view

override var accessibilityViewIsModal: Bool {
    get { return true }
    set {}
}

老实说,它对我没有用,我们 UIPresentationController.

对齐不可见框架


读取顺序是按帧计数的,有时会产生意外的结果。例如,我们放大了“ i”按钮的框架以使其更易于单击。但事实证明,它比披萨名称的框架要高,因此“ i”按钮成为了第一个焦点元素。尽管它很小,但它在右边,通常并不那么重要。



您可以通过更改阅读顺序accessibilityElements,但我将显示另一种方式。VoiceOver使用属性accessibilityFrame,默认情况下,它与通常的帧匹配。有几种解决方案:

  1. 重写控件子类并返回一个减小的值。
  2. 在外面设置正确的框架。
  3. 只需调整框架,使其与铭文齐平即可。

但是,重要的一点是,此框必须在屏幕的坐标中。为了简单转换,有一个功能UIAccessibility.convertToScreenCoordinates

它也可以用来组合控件。例如,您需要组合切换器及其签名,因此该元素将变大,单击起来会更容易,不必要的重复会消失。

override func layoutSubviews() {
        super.layoutSubviews()
        repeatSwitch.accessibilityLabel = repeatLabel.text
        repeatSwitch.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(
                repeatSwitch.frame.union(repeatLabel.frame).insetBy(dx: -12, dy: -12),
                in: repeatSwitch.superview!)
}

我还使用扩大了焦点.inset,按起来更方便。



使用框架,AccessibilityContainer您可以使图形和表格可用。

总结主要动作


这是有关UX的更多信息,不过我还是会告诉您。视力正常的人很容易从屏幕上读取所有设置,但是对于这个盲人,您需要手动分类所有控件。您可以简化此过程,并在“购买”按钮中总结所有更改。

例如,获取“购买”按钮。加入培根,去除墨西哥胡椒。价格434美元 »,您无需在代码中编写任何异常代码,只需收集添加/删除的行即可:

accessibilityTraits = .button
accessibilityLabel = ""
accessibilityValue = " ,  .  434₽" 

而且不要忘了在下载指示器上签名


如果将产品添加到购物车后,您短暂地阻塞了界面并下载了某些内容,请不要忘记使下载指示器可用:

  1. 使用警报将重点放在指示器上。
  2. 给焦点命名。例如,加载。
  3. 说吧accessibilityViewIsModal
  4. 下载完成时通知我。

如果在加载后显示一个新屏幕,则VoiceOver本身将切换焦点,因此很明显下载已结束。在更复杂的情况下,您可以明确地说出这句话,甚至添加振动

苹果做什么?
有趣的是,Safari在iOS 13中与此协同工作:在页面加载期间,它每秒单击一次,而在页面加载时,它会*哇*。las,从api端来看这还不可用,我们正在等待iOS 14。



为了适应屏幕,我们使用了不同的方法:更改按钮的顺序,指示要聚焦的第一个元素,固定模式窗口,汇总主按钮上的设置,并努力简化应用程序。这些知识足以适应几乎所有应用。前进。

下次,我将讨论VoiceOver,语音控制和UI测试的实现之间的区别。
为了不错过下一篇文章,请订阅我的Dodo Pizza Mobile频道

All Articles