iOS 無(wú)障礙化(適老化)適配總結(jié)

原文地址: iOS 無(wú)障礙化(適老化)適配總結(jié)

VoiceOver 和 Accessibility

iOS 開(kāi)發(fā)中主要討論的是 UIAccessibility 的 API 在 VoiceOver 上的運(yùn)用.

旁白使用手冊(cè)-在 iPhone 上學(xué)習(xí)旁白手勢(shì)

檢測(cè)當(dāng)前是否開(kāi)啟無(wú)障礙模式

系統(tǒng)通知:UIAccessibilityVoiceOverStatusDidChangeNotification

系統(tǒng)方法:UIAccessibilityIsVoiceOverRunning()

iOS代碼適配示例

UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
// 描述控件是什么 第一個(gè)播放
titleLabel.accessibilityLabel = @"標(biāo)簽";
// 元素的特征 如按鈕、鏈接等 第二個(gè)播放
titleLabel.accessibilityTraits = UIAccessibilityTraitStaticText;
// 附加說(shuō)明 第三個(gè)播放, 只在真機(jī)播放, 且有一秒延遲
titleLabel.accessibilityHint = @"提示";
// 元素的frame(基于屏幕坐標(biāo)系, 一般不用填設(shè)置), 當(dāng)系統(tǒng)聚焦到當(dāng)前元素時(shí), 會(huì)有一個(gè)黑色的外框, 該值就是聚焦框的大小. 當(dāng)元素過(guò)小時(shí)可以通過(guò)設(shè)置該 frame 使得容易點(diǎn)擊, 這個(gè)不會(huì)改變 app 的 UI. 默認(rèn)為 CGRectZero.
titleLabel.accessibilityFrame = CGRectMake(0, 0, 100, 100);
// 元素的值 用在UISlider,UITextField等組件上 用來(lái)描述元素的值
titleLabel.accessibilityValue = @"60%";
// VoiceOver會(huì)把這幾個(gè)屬性連接起來(lái), 朗讀順序?yàn)閘abel→value(可選)→traits→hint.

需要適配的場(chǎng)景

由于系統(tǒng)控件是默認(rèn)處理好的,而且VoiceOver的默認(rèn)閱讀順序通常也沒(méi)什么大問(wèn)題,因此需要開(kāi)發(fā)者專門(mén)去兼容的場(chǎng)景有如下

自定義 View 組件無(wú)障礙化

當(dāng)子元素需要配置成 accessible, 而你的視圖容器不需要配置成 accessible, 下面兩個(gè)方法可以解決

  1. 直接設(shè)置 accessibilityElements 屬性 (iOS8+), 使用在代碼里搜下吧, 很簡(jiǎn)單.
  2. (很老的 API 了, 不推薦使用)視圖需要實(shí)現(xiàn) UIAccessibilityContainer.

只有背景圖片無(wú)文字的控件

需要給出描述和對(duì)應(yīng)控件的屬性, 例如返回按鈕/關(guān)閉按鈕

self.backButton.accessibilityLabel = @"返回";
self.closeButton.accessibilityLabel = @"關(guān)閉";
self.closeButton.accessibilityTraits = UIAccessibilityTraitButton;

讀取時(shí)間的無(wú)障礙

24 小時(shí)制的時(shí)刻系統(tǒng)無(wú)障礙會(huì)按字符一個(gè)個(gè)讀出,需要轉(zhuǎn)化成 12 小時(shí)制的寫(xiě)法,才會(huì)正常讀取時(shí)間,例如 22:58 要寫(xiě)成 10.58PM.

某些控件的無(wú)障礙元素默認(rèn)是關(guān)閉的, 需要開(kāi)啟

UIView/UIImageViewisAccessibilityElement 默認(rèn)為 NO
而 UILabel/UIButton/UISwitch/UICollectionViewCell/UITableViewCell/UIPageControl 等組件默認(rèn)為 YES.
其中 UILabel 比較離譜, 偶現(xiàn) setText, 然后 po isAccessibilityElement 為 YES, 但是不響應(yīng)的情況, 最好都顯式設(shè)置 isAccessibilityElement = YES 吧.

父 View 如果為 AccessibilityElement, 子 element 將不響應(yīng) VoiceOver

self.bottomView.isAccessibilityElement = YES;
self.bottomView.accessibilityLabel = @"XXX";

比如一個(gè)View中有多個(gè)Label,那么每一個(gè)下面的Label單獨(dú)訪問(wèn)可能意義不大,那么就可以將這個(gè)View設(shè)置成可以訪問(wèn)的,然后將其accessibilityLabel設(shè)置為所有子Label的 accessibilityLabel的合并值.

無(wú)障礙控件點(diǎn)擊區(qū)域過(guò)小

類似下圖:圖片和文字搭配, 需要擴(kuò)大無(wú)障礙點(diǎn)擊范圍:設(shè)置accessibilityFrame,通過(guò)CGRectUnion(frame1,frame2);
需要注意的是 accessibilityFrame 是相對(duì)屏幕的坐標(biāo)系的, 使用 [UIView convertRect:toView:] 來(lái)轉(zhuǎn)一次才能設(shè)置, 如果用了自動(dòng)布局會(huì)很麻煩.

UI 組件實(shí)際功能不符合時(shí)

使用 button 只做點(diǎn)擊作用的時(shí)候(不需要選中態(tài)),需要設(shè)置對(duì)應(yīng)的控件屬性 accessibilityTraits

// 1. 不需要播報(bào)"已選中"
button.accessibilityTraits &= ~UIAccessibilityTraitSelected;
// 2. 純文本
button.accessibilityTraits = UIAccessibilityTraitStaticText;

使用 laber / view / imageView, 實(shí)現(xiàn)自定義響應(yīng)事件時(shí),設(shè)置對(duì)應(yīng)控件屬性 accessibilityTraits 為 UIAccessibilityTraitButton.

hidden 元素

有時(shí)把某個(gè) view 設(shè)成 hidden 的時(shí)候, UI 上已經(jīng)不展示了, 但是VoiceOver仍然可以讀到.
此時(shí)可以使用

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil)

強(qiáng)制更新VoiceOver的表現(xiàn).

主動(dòng)播報(bào)

toast 彈出或者刷新成功等場(chǎng)景需要主動(dòng)播報(bào), 如下:

UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, voiceText);

彈窗

彈窗后屏蔽彈窗下面的元素

popupView.accessibilityViewIsModal = YES;//當(dāng)前view才能響應(yīng)無(wú)障礙播報(bào)
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, popupView.accessibilityElements.firstObject);

焦點(diǎn)亂跳

焦點(diǎn)亂跳有兩個(gè)可能

  1. UICollectionView/UITableView reloadData 時(shí)候焦點(diǎn)會(huì)跳到最后一個(gè) Cell, 解決辦法為自行實(shí)現(xiàn) cell 的更新, 減少直接 reloadData 調(diào)用; 如果這個(gè) Cell 是滿屏的, 可以設(shè)置當(dāng)前 Cell 的 accessibilityViewIsModal 簡(jiǎn)單解決.
  2. 某個(gè) UI 元素被加入到 accessibilityElements 但是又被標(biāo)記為不可用 isAccessibilityElement = NO, 在手指左劃時(shí)會(huì)出現(xiàn)焦點(diǎn)亂跳(但是右劃正常)

UICollectionViewDelegate 異常

在無(wú)障礙開(kāi)啟時(shí), UICollectionViewDelegate 部分回調(diào)異常, 表現(xiàn)為 Cell 提前預(yù)加載了, 不在這些方法中執(zhí)行關(guān)鍵邏輯即可, 目前已知下面兩個(gè)回調(diào)會(huì)有異常

- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;

Cell 橫劃刪除 在無(wú)障礙下無(wú)法使用

使用自定義操作來(lái)代替

UIAccessibilityCustomAction * action = [[UIAccessibilityCustomAction alloc] initWithName:@"測(cè)試" target:self selector:@selector(testToast)];
UIAccessibilityCustomAction * action1 = [[UIAccessibilityCustomAction alloc] initWithName:@"測(cè)試1" target:self selector:@selector(testToast1)];
UIAccessibilityCustomAction * action2 = [[UIAccessibilityCustomAction alloc] initWithName:@"測(cè)試2" target:self selector:@selector(testToast2)];

self.accessibilityCustomActions = @[action, action1, action2];

觸發(fā)方法:

  1. 單擊選中該 UI 元素
  2. 單指上下劃, 第一次播報(bào) "測(cè)試", 再單指上下劃第二次播報(bào) "測(cè)試2", 繼續(xù)劃會(huì)有第三次播報(bào) "測(cè)試3", 第四次播報(bào)"激活" (初始狀態(tài)).
  3. 然后在某一次播報(bào)后單指雙擊, 就可以觸發(fā)對(duì)應(yīng)的 selector

需要播報(bào)"已選中"

// 設(shè)置"已選中"播報(bào)
view.accessibilityTraits |= UIAccessibilityTraitSelected;
// 移除"已選中"播報(bào)
view.accessibilityTraits &= ~UIAccessibilityTraitSelected;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,415評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,104評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,884評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,647評(píng)論 6 408
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,130評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,366評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,887評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,737評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,939評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,174評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,586評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,827評(píng)論 1 283
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,608評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,914評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容