關於支援 VoiceOver

yang152412發表於2018-12-10

Voice Over 是蘋果出的為方便視力障礙人士使用手機的功能,開啟後就可以把螢幕上的內容用語音讀出來。通常開發的 app 很少考慮到這個功能,也沒有做適配。但是 UILabelUIButton 本身是支援 Voice Over的,不用做適配都可以自動讀出來。但是自定義 View 這樣的還是需要做一下適配會體驗比較好一點。

一、適配

適配 VoiceOver 最重要的幾個方法就是

@property(nonatomic) BOOL isAccessibilityElement;

@property(nonatomic, copy) NSString *accessibilityLabel;

@property(nonatomic, copy) NSString *accessibilityHint;

@property(nonatomic) UIAccessibilityTraits accessibilityTraits;

@property(nonatomic, copy) NSString *accessibilityValue;

accessibilityLabel(標籤)一個簡單的詞或短語,它簡潔明瞭地描述控制元件或者檢視,但是不能識別元素型別,UIAccessibilityElement必須要有這個屬性。例如“新增”、“播放”。

accessibilityHint(提示)一個簡單的詞或短語,描述發生在元素上動作的結果。例如“新增標題”或者“開啟購物列表”。

accessibilityValue(值)不是由標籤定義的元素時的當前值。僅當元素的內容是可改變並且不能使用label描述時,一個無障礙元素才需要為其賦值。例如,一個進度條的標籤可以是”播放進度”,但是它當前的值是“50%”。

accessibilityTraits這個element的型別以及狀態,就是通過traits來表徵這個Element的特質,資料型別是一個列舉型別,可以通過按位或的方式合併多個特性。

VoiceOver會把這幾個屬性連線起來,朗讀順序為label→value(可選)→traits→hint。但需要注意的是,當某個View的是AccessibilityElement的時候 ,其subviews都會被遮蔽掉,如果想要都讀出來,只能改變他們的層次結構,並都設定isAccessibilityElement為YES。這個特性還是挺有用的,比如一個View中有多個Label,那麼每一個下面的Label單獨訪問可能意義不大,那麼就可以將這個View設定成可以訪問的,然後將其accessibilityLabel設定為所有子Label的 accessibilityLabel的合併值。

這幾個屬性都是可以賦值也可以過載。不過推薦過載,這樣看起來更清晰。 而且對於accessibilityLabel 的過載也有下面的好處:

1、這個是在 Voice Over聚焦到控制元件的時候才去呼叫,那麼如果過載的話,就是類似於懶載入這種了,用到的時候才去初始化。

2、對於自定義 View,包括 cell,會有很多狀態,顯示的內容都是根據狀態來的,那麼這時候過載的話,就可以把這部分程式碼寫在一起,和 正常的程式碼區分開。

二、Texture 的適配

關於 Texture 的自定義 view,可以指定 isAccessibilityContainer == YES,指定這個屬性為 YES之後,Texture 可以自動把這個 view 下面支援 Voice Over的子控制元件給組合起來,形成一個整體,並且子控制元件有 button 的時候,實現了 一個手指上下輕掃 響應自定義事件,分成方便。

但是有個問題就是 Texture 本身沒有處理 控制元件的 accessibilityElementsHidden 屬性。而且也沒有判斷子控制元件的狀態是顯示還是隱藏,只是 遍歷了子控制元件,能讀的就給讀出來。所以 Texture的適配就是 判斷隱藏的子控制元件,不能用隱藏方法,只能是直接不新增,不呼叫addSubnode

三、播放自定義語音,自動讀出Toast內容

直接上程式碼:

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 來直接加入購物車。

六、提示框 Alert & Actionsheet

系統的 UIAlert 和 UIActionSheet 當然系統做了自動適配,沒有問題。問題在於 自定義的 Alert 和 ActionSheet。

1、如果是 只用 用 addSubviewkeyWindow的話,那麼 提示框出來之後壓根就不會自動聚焦讀出,只能手指點上去才會讀出,這個對盲人不現實。

2、如果用了自己生成的一個 window,然後把 alert 新增到這個 window 上的話,那麼會有自動聚焦,再顯示彈出框的時候可以自動讀出,但是當手指不小心碰到空白區域後,焦點就會跑到背景上下面正常顯示的 view,會讀出背景後面的內容。

為了解決這個問題,那麼對於自定義的 Alert 都改為了 利用 presentViewController的方式來實現。可以自動讀出,也不會點到空白區域就失去焦點。見這裡

參考:

blog.csdn.net/heyc861221/…

blog.csdn.net/u010850094/…

blog.csdn.net/heyc861221/…

www.logcg.com/archives/23…

相關文章