VoiceOver的使用

简介

VoiceOver是苹果“读屏”技术的名称,属于辅助功能的一部分。VoiceOver可以读出屏幕上的信息,以帮助盲人进行人机交互。 这项技术在苹果的各个系统中都可以看到,OS X,iOS,watchOS,甚至tvOS。 苹果公司的VoiceOver在2015年6月18日获得了美国盲人基金会(American Foundation for the Blind, AFB)颁发的海伦凯勒成就奖,成为全球首家获得此殊荣的科技公司。 单从iOS来说,iOS的VoiceOver功能可以毫不夸张的说是三大移动平台中做的最好的。

虽然说苹果默认的UI组件都已经默认支持VoiceOver功能了,但是通常情况下App还是需要对VoiceOver进行适配和优化的,比如说一些自定义复杂UI组件。

基本使用

iPhone上开启VoiceOver功能后,就可以通过 单指左右轻扫 来遍历当前界面中的所有的AccessibilityElement(可以被VoiceOver访问的UI元素),当一个AccessibilityElement被选中后,VoiceOver会将AccessibilityElement的信息读出来。 单指轻点两次 能够激活当前元素对应的操作,比如当前AccessibilityElement是一个按钮,那么对应的就是按钮的Action事件。

简单点来说在App开发过程中关于VoiceOver我们需要关注如下几点:

  • 界面上的AccessibilityElement有哪些
  • AccessibilityElement的位置和形状
  • AccessibilityElement的信息是什么(就是Element被选中后,被读出来内容)
  • AccessibilityElement所能响应的的事件有哪些

UIKit中的控件基本都是 VoiceOver Ready的,即使是UIView,你也可以通过简单的设置其实变成AccessibilityElement。所以这一小节中所讲的AccessibilityElement其实都是UIView或其子类的实例。

相关属性和方法基本都在UIAccessibility.h这个头文件中进行了声明。

所以简单的情况下通过UIView的isAccessibilityElement属性就可以控制某个View是否是AccessibilityElement,在UIKit的控件中,像UILabel,UIButton 这些控件的isAccessibilityElement属性默认就是true的,UIView这个属性默认是false。

一般情况下AccessibilityElement的位置和形状是通过accessibilityFrame进行设置的,默认值是View在屏幕中的位置,形状就是View的矩形形状。如果你想自己设置accessibilityFrame的值,那么得注意下,这边的frame值是相对于设备Screen的坐标系的,当然可以通过UIAccessibilityConvertFrameToScreenCoordinates函数来帮助转换。此函数有两个参数,一个rect,一个是view,
其含义就是将相对于view这个坐标系的rect转换成相对于screen坐标系的值并返回。所以一般情况下 rect可以是目标Element在父View中的frame,view就为其父view。


1

public func UIAccessibilityConvertFrameToScreenCoordinates(rect: CGRect, _ view: UIView) -> CGRect

如果你想设置非矩形的形状,你也可以通过给 accessibilityPath 属性指定一个UIBezierPath类型的值来自定义AccessibilityElement的形状。

至于AccessibilityElement的信息可以通过下面几个UIAccessibility的属性来决定

  • accessibilityLabel 这是什么
  • accessibilityHint 这个有什么用,会产生什么样的结果
  • accessibilityValue 这个的 值 是什么
  • accessibilityTraits 这个的类型以及状态,就是通过traits来表征这个Element的特质,数据类型是一个枚举类型,可以通过按位或的方式合并多个特性。

这里有个需要注意的就是,当某个View的是AccessibilityElement的时候 ,其subviews都会被屏蔽掉,这个特性有时候还是有用的,比如一个View中包含多个Label,那么你希望每一个下面的Label不要单独可以访问到,那么你可以将这个View设置成可以访问的,然后将其accessibilityLabel设置为所有子Label的accessibilityLabel的合并值。

至于AccessibilityElement的事件,最简单的莫过于上面提到 单指轻点两次 能够激活当前元素对应的操作了,如果当前AccessibilityElement实现的public func accessibilityActivate() -> Bool这个方法返回true,那边此逻辑将被调用,否则相当于在AccessibilityElement的accessibilityActivationPoint这个位置点上进行了一次Tap操作。

进阶应用

Accessibility Container 设想下这样的一个场景,一个UIView,内部包含一组用户可以进行交互的内容,每一个内容之间是独立的,但是这些内容不是以子View的形式存在,而是通过Quarz 2D或者Core Text渲染而成,所以这部分内容无法通过上面的方式变成AccessibilityElement。这种情况UIView需要按照UIAccessibilityContainer的方式,来将内部的每一个独立的内容都描述成UIAccessibilityElement的实例,这个时候这个UIView我们称之为Accessibility
Container。

接下来讲解具体的实现步骤了。 先说说iOS 8之后如何实现,首先Accessibility Container的isAccessibilityElement值必须设置为false,另外我们需要创建出所有的UIAccessibilityElement的实例,然后赋给accessibilityElements属性(iOS 8.0+) 假设在一个UIView的子类中,通过叫做updateAccessibleElements的方法来更新维护所有的UIAccessibilityElement实例,当界面上的内容发生变化,或者VoiceOver开启关闭状态发生变化时,调用此方法以更新accessibilityElements,相关伪代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

internal func updateAccessibleElements() {

   guard UIAccessibilityIsVoiceOverRunning() else {

        self.accessibilityElements = nil

        return

   }

        

   self.isAccessibilityElement = false

   var elements = [AnyObject]()

   let element1 = UIAccessibilityElement(accessibilityContainer: self)

   element1.accessibilityLabel = "element1"

   element1.accessibilityTraits = UIAccessibilityTraitStaticText

   element1.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(element1FrameInSelf, self)

   elements.append(element1)

        ...

   self.accessibilityElements = elements

}

如果是iOS 8以下,那么就需要实现下面的几个方法来实现了,比较繁琐:


1

2

3

4

5

6

// accessibilityElement的个数

public func accessibilityElementCount() -> Int

// 返回指定Index的accessibilityElement

public func accessibilityElementAtIndex(index: Int) -> AnyObject?

// 返回指定accessibilityElement的Index

public func indexOfAccessibilityElement(element: AnyObject) -> Int

使用上面第二种方式的时候,往往需要自己维护一个包含所有accessibilityElements的数组,然后通过数组来完成上面的这三个方法的返回。这种方法比较累赘,而且当界面更新需要刷新内容的时候,还需要发送通知系统,告诉系统界面上的accessibilityElements有变动需要更新。所以最小版本定位于iOS 8的情况下还是直接设置accessibilityElements属性的方式比较科学。

关于UIAccessibilityElement这个类的设计还是存在一些疑惑的,既然NSObject的UIAccessibility扩展已经包含了诸如accessibilityLabel,accessibilityHint这些属性,为何UIAccessibilityElement类中还需要重复声明这些属性,有点重复的感觉。

Actions

之前只讲到了最简单的事件,就是单指轻点两下,其实常见的Actions有下面这些,每一个Action都会对应一个方法,可以通过覆盖方法的方式来自定义Action对应的逻辑:

  • Activate 单指轻点两次

1

public func accessibilityActivate() -> Bool

  • Escape. 单指 Z-shaped 手势一般用于退出模态界面或者返回导航的上一页界面

1

public func accessibilityPerformEscape() -> Bool

  • Magic Tap. 双指轻点两次触发 most-intended action.

1

public func accessibilityPerformMagicTap() -> Bool

  • Three-Finger Scroll. 三指滑动触发界面水平或者垂直的滚动

1

public func accessibilityScroll(direction: UIAccessibilityScrollDirection) -> Bool

  • Increment. 单指向上滑动,需要设置accessibilityTraits为UIAccessibilityTraitAdjustable,否则对应的方法不会被调用

1

public func accessibilityIncrement()

  • Decrement. 单指向下滑动,需要设置accessibilityTraits为UIAccessibilityTraitAdjustable,否则对应的方法不会被调用

1

public func accessibilityDecrement()

这些方法中,其中Escape,Magic Tap,Three-Finger Scroll这几种手势支持在响应链中向上寻找对应的Action方法,首先用户在屏幕上进行相应的手势,系统检查当前VoiceOver的Focus的Element有无实现对应的方法,没有实现的话,则向响应链的上一级寻找。比如有些全局性的操作,对应的方法写在上层View或者ViewController中比较合适。 其实很多系统提供的组件都默认实现了一些VoiceOver的手势Action,比如UINavigationController,
UIAlertController 都提供了对Escape手势的支持。

Accessibility Notification

Accessibility提供了一系列的通知,可以完成一些特定的需求。比如你可以监听UIAccessibilityVoiceOverStatusChanged通知,来监控Voice Over功能开启关闭的实时通知 。

或者是你在App中主动发送一些通知,来让系统做出一些变化,比如当你界面上的AccessibilityElement有变动的时候你可以发送UIAccessibilityLayoutChangedNotification通知,通知发送时使用UIAccessibility的专用的函数,UIAccessibilityPostNotification函数有两个参数,第一个是通知名,第二个是你想让VoiceOver读出来的字符串或者是新的VoiceOver的焦点对应的元素。


1

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.myFirstElement)

个人思考

AccessibilityElement的信息尽量简洁,accessibilityLabel不要包含提示性质的文案,避免信息干扰

TableView的每一个Cell的信息尽量合并,使得Cell变成一个整体的AccessibilityElement,避免无意义的冗余元素之间切换的操作。Cell中有多个按钮的时候,可以考虑使用Magic Tap的方式,Magic Tap的Action中弹出sheet样式的UIAlertController来供用户操作。

自定义的模态页面注意设置accessibilityViewIsModal为true,最好支持Escape手势 的方式退出模态页面。

将页面中装饰用的没有实际意义的元素的accessibilityElementsHidden设置成true,减少操作过程中的干扰

时间: 2024-11-03 23:13:35

VoiceOver的使用的相关文章

ios辅助功能之voiceover实战

一个元素朗读的内容可分为以下4个部分(4部分按先后顺序朗读) 1. Label:元素的标题 2. Value:元素的值(可选) 3. Traits:元素的特征,即类型,包含: 按钮/链接/搜索框/键盘按键/图片/播放音乐/选择/总觉元素/频繁更新/不可用空 4. Hint:提示(可选,与Traits之间会停顿一小段时间) 例子中~表示停顿 实例①:点击首页的一份书籍 Label:仙逆 Value:(无) Traits:按钮 Hint:轻点两下进入书籍详情 朗读内容:仙逆~按钮~~轻点两下进入书籍

《iOS Human Interface Guidelines》——VoiceOver

VoiceOver(旁白) VoiceOver增加对盲人.低视力和有学习挑战的用户的亲近度. 为了确保VoiceOver用户可以使用你的app,你也许需要在你的用户界面提供一些对视图和控件的描述信息.支持VoiceOver不需要你改变任何你的UI视觉设计. 当你通过完全标准的方式使用标准UI元素的时候,你就几乎不需要做额外的工作.你的UI越定制化,你就需要提供越多的定制信息使VoiceOver可以准确地描述你的app. 让你的iOS app亲近VoiceOver用户可以增加你的用户基础和帮助你进

Mac VoiceOver + react 注意事项

苹果提供的 VoiceOver 针对不便人员或者特殊使用习惯的用户帮助很大. 打开Mac VoiceOver 快捷键: command + F5 为了能够支持VoiceOver, 在前段程序中一些写法需要注意. 1. chrome 对于<a/> 能够自动 tab 上,无论是否手动添加 tabIndex. 上面的写法,在chrome 里面能够被 screen reader 自动识别,并且能够通过 tab 进行切换 2. Safari 对于设置了 href 的<a/> 无论如何都无法

图文解释XCode常用快捷键的使用

刚开始用Xcode是不是发现以前熟悉的开发环境的快捷键都不能用了?怎么快捷运行,停止,编辑等等.都不一样了.快速的掌握这些快捷键,能提供开发的效率. 其实快捷键在Xcode的工具栏里都标注有,只是有的符号和你的键盘上的符号对应不起来罢了.下面截图工具栏里的快捷键总结一下常用快捷键的用法. 一.关于运行调试 1.运行,停止,都在工具栏的Product里. Command + R  运行. Command + .  停止 2.F6单步调试.F7跳入,F8继续, 和Eclipse,VS类似 二.导航

OS快捷键

OS X 键盘快捷键 若要使用键盘快捷键或按键组合,您可以同时按修饰键和字符键.例如,同时按下 Command 键(标有 符号的按键)和“c”键会将当前选中的任何内容(文字.图形等)拷贝至夹纸板.这也称作“Command-C 按键组合”(或键盘快捷键). 许 多按键组合中都包含修饰键.修饰键将改变 OS X 对其他键击或鼠标/触控板点按动作的解释方式.修饰键包括:Command.Shift.Option.Control.Caps Lock 和 Fn 键.如果您的键盘有 Fn 键,可能需要在以下列

fastclick源码学习(1)

1.入口 1 FastClick.attach(document.body) 2.初始化参数 function FastClick(layer, options) { var oldOnClick; options = options || {}; this.trackingClick = false; this.trackingClickStart = 0; this.targetElement = null; this.touchStartX = 0; this.touchStartY =

Xcode及Mac快捷键

1. 文件 CMD + N: 新文件CMD + SHIFT + N: 新项目CMD + O: 打开CMD + S: 保存CMD + SHIFT + S: 另存为CMD + W: 关闭窗口CMD + SHIFT + W: 关闭文件 2. 编辑 CMD + [: 左缩进CMD + ]: 右缩进 CMD + CTRL + LEFT: 折叠CMD + CTRL + RIGHT: 取消折叠CMD + CTRL + TOP: 折叠全部函数CMD + CTRL + BOTTOM: 取消全部函数折叠CTRL +

Flipboard web移动端-打造每秒60帧的流畅体验

在智能手机和平板电脑的黎明时期, Flipboard 推出“移动先行”的体验,使我们可以重新思考页面中内容布局的原则,以及与触摸屏相关的,如何获得更好的用户体验的因素. 为了建立完整的体验,我们将 Flipboard 带到 web 端.我们在 Flipboard 所做的,在每台用户使用的设备上都拥有独立的价值:整理那些来自你最关心的主题,来源以及人的最好的故事.因此把我们的服务带到web端,也是一个合乎逻辑的延伸. 当我们开始这个项目后,认识到我们需要把源自移动体验的思考搬到 web 端,试图提

iphone 获取硬件型号

这个写原创不知道会不会被人打,要想获取手机硬件的型号 + (NSString *)platform { size_t size; sysctlbyname("hw.machine", NULL, &size, NULL, 0); char *machine = malloc(size); sysctlbyname("hw.machine", machine, &size, NULL, 0); NSString *platform = [NSStrin