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
的方式來實現。能夠自動讀出,也不會點到空白區域就失去焦點。見這裏。