Voice Over
是苹果出的为方便视力障碍人士使用手机的功能,打开后就能够把屏幕上的内容用语音读出来。一般开发的 app 不多考虑到这个功能,也没有作适配。可是 UILabel
和 UIButton
自己是支持 Voice Over
的,不用作适配均可以自动读出来。可是自定义 View 这样的仍是须要作一下适配会体验比较好一点。html
适配 VoiceOver
最重要的几个方法就是node
@property(nonatomic) BOOL isAccessibilityElement;
bash
@property(nonatomic, copy) NSString *accessibilityLabel;
app
@property(nonatomic, copy) NSString *accessibilityHint;
post
@property(nonatomic) UIAccessibilityTraits accessibilityTraits;
atom
@property(nonatomic, copy) NSString *accessibilityValue;
spa
accessibilityLabel(标签)
一个简单的词或短语,它简洁明了地描述控件或者视图,可是不能识别元素类型,UIAccessibilityElement必需要有这个属性。例如“添加”、“播放”。.net
accessibilityHint(提示)
一个简单的词或短语,描述发生在元素上动做的结果。例如“添加标题”或者“打开购物列表”。code
accessibilityValue(值)
不是由标签订义的元素时的当前值。仅当元素的内容是可改变而且不能使用label描述时,一个无障碍元素才须要为其赋值。例如,一个进度条的标签能够是”播放进度”,可是它当前的值是“50%”。htm
accessibilityTraits
这个element的类型以及状态,就是经过traits来表征这个Element的特质,数据类型是一个枚举类型,能够经过按位或的方式合并多个特性。
VoiceOver
会把这几个属性链接起来,朗读顺序为label→value(可选)→traits→hint。但须要注意的是,当某个View的是AccessibilityElement的时候 ,其subviews都会被屏蔽掉,若是想要都读出来,只能改变他们的层次结构,并都设置isAccessibilityElement为YES。这个特性仍是挺有用的,好比一个View中有多个Label,那么每个下面的Label单独访问可能意义不大,那么就能够将这个View设置成能够访问的,而后将其accessibilityLabel设置为全部子Label的 accessibilityLabel的合并值。
这几个属性都是能够赋值也能够重载。不过推荐重载,这样看起来更清晰。 并且对于accessibilityLabel
的重载也有下面的好处:
一、这个是在 Voice Over
聚焦到控件的时候才去调用,那么若是重载的话,就是相似于懒加载这种了,用到的时候才去初始化。
二、对于自定义 View,包括 cell,会有不少状态,显示的内容都是根据状态来的,那么这时候重载的话,就能够把这部分代码写在一块儿,和 正常的代码区分开。
关于 Texture
的自定义 view,能够指定 isAccessibilityContainer == YES
,指定这个属性为 YES
以后,Texture
能够自动把这个 view 下面支持 Voice Over
的子控件给组合起来,造成一个总体,而且子控件有 button 的时候,实现了 一个手指上下轻扫 响应自定义事件,分红方便。
可是有个问题就是 Texture
自己没有处理 控件的 accessibilityElementsHidden
属性。并且也没有判断子控件的状态是显示仍是隐藏,只是 遍历了子控件,能读的就给读出来。因此 Texture
的适配就是 判断隐藏的子控件,不能用隐藏方法,只能是直接不添加,不调用addSubnode
。
直接上代码:
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, message);
复制代码
利用这个功能,能够读出 Toast
的内容,目前toast
控件,大部分都是直接 addSubview 到UIWindow
上的,这样实现不会自动聚焦,直到自动消失以后也读不出来。能够用这个方法来读出 TOAST
的 内容,或者其余的提示。
可是有个主意的地方就是若是当前正在播报内容,这个时候直接调用时读不出来的,因此加了个延迟
hud.isAccessibilityElement = YES;
hud.accessibilityLabel = message;
hud.accessibilityTraits = UIAccessibilityTraitStaticText;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, message);
});
复制代码
直接上代码
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, VIEW);
复制代码
UIAccessibilityCustomAction
,用这个类来初始化来实现自定义的事件,自定义事件在 单个手指上下轻扫后 出来,若是有多个的话,继续上下轻扫来切换,而后再单个手指 双击来响应该事件(例如 UITableViewCell 的删除事件)。
初始化后,能够绑定到控件的
@property (nullable, nonatomic, strong) NSArray <UIAccessibilityCustomAction *> *accessibilityCustomActions NS_AVAILABLE_IOS(8_0);
复制代码
属性上。这个属性是 NSObject
的扩展。因此 UIKit
的控件应该都支持。
这个属性针对 Cell
的适配很是有用。例如 电商类的 app,一个 cell 也许是一个商品,这时候能够把整个 cell 设置 isAccessibilityElement
,指定为 AccessibilityElement
,成为一个总体,而后组合商品名字,价格属性等 来读出,而后 利用 accessibilityCustomActions
来直接加入购物车。
系统的 UIAlert 和 UIActionSheet 固然系统作了自动适配,没有问题。问题在于 自定义的 Alert 和 ActionSheet。
一、若是是 只用 用 addSubview
到 keyWindow
的话,那么 提示框出来以后压根就不会自动聚焦读出,只能手指点上去才会读出,这个对盲人不现实。
二、若是用了本身生成的一个 window,而后把 alert 添加到这个 window 上的话,那么会有自动聚焦,再显示弹出框的时候能够自动读出,可是当手指不当心碰到空白区域后,焦点就会跑到背景上下面正常显示的 view,会读出背景后面的内容。
为了解决这个问题,那么对于自定义的 Alert 都改成了 利用 presentViewController
的方式来实现。能够自动读出,也不会点到空白区域就失去焦点。见这里。