iOS-UIAccessibility旁白適配

近期在處理App適配VoiceOver(旁白)功能,簡(jiǎn)單的系統(tǒng)控件蘋果都能很好的識(shí)別,但在復(fù)雜界面處理遇到了一些問題,VoiceOver問題可參考的資料不多,把自己踩過的坑記錄如下。

首先介紹下 旁白常用的手勢(shì)

  • 輕點(diǎn):選擇并朗讀項(xiàng)目。
  • 左右輕掃:選擇下一項(xiàng)或上一項(xiàng)。
  • 雙指輕點(diǎn)三下:打開“項(xiàng)目選取器”。
  • 三指上下輕掃:一次滾動(dòng)一頁。
  • 三指左右輕掃:前往下一頁或上一頁(例如,在主屏幕上中)。
  • 四指滑動(dòng):切換App 。
    這是本人在開發(fā)旁白模式中常用的手勢(shì),更多手勢(shì)可以查看 iPhone使用手冊(cè) 來了解。

旁白最常用 UIAccessibility API

  • accessibilityLabel: 描述內(nèi)容“是什么”,例如 “產(chǎn)品名稱”
  • accessibilityValue: 描述“值是多少”,例如“年化利率5%”
  • accessibilityTraits: 描述"特性、狀態(tài)、行為、用法"??丶奶匦钥梢允前粹o(Button),控件的狀態(tài)可以是已選中(Selected)
  • accessibilityHint: 描述行動(dòng)“做什么”,例如“點(diǎn)擊兩下可跳轉(zhuǎn)購買頁面”

1、Label、Value、Hint 配置的字符串,會(huì)以語音的形式念出來,配置什么,念什么。
2、 配置的Traits各個(gè)類型,也會(huì)以語音的形式念出。例如:UIAccessibilityTraitSelected、UIAccessibilityTraitImage、UIAccessibilityTraitButton分別播報(bào)"已選定”、”圖片”、”按鈕”。

遇到的問題記錄:

1.是否啟用VoiceOver
UIAccessibilityIsVoiceOverRunning()
2.主動(dòng)播報(bào)

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

UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, @"播報(bào)文案xxxx");
3.焦點(diǎn)穿透

accessibilityViewIsModal 屬性設(shè)為YES

self.noNetworkV.accessibilityViewIsModal = YES;
4.兼容開發(fā)者自定義的控件和視圖,支持VoiceOver

所有的標(biāo)準(zhǔn)UIKit控件和視圖默認(rèn)支持VoiceOver,UILabel,UIButton這些控件的isAccessibilityElement屬性默認(rèn)是true, UIView屬性默認(rèn)是false,所以自定義視圖如果想要支持VoiceOver,需要設(shè)置 isAccessibilityElement = YES, 使得VoiceOver可以訪問這個(gè)view。

 view.isAccessibilityElement = YES;
 view.accessibilityLabel = @"文案內(nèi)容";
 view.accessibilityTraits = UIAccessibilityTraitNone;
5.訪問Cell與Cell中的按鈕,且分別讀出不同的內(nèi)容

假設(shè)現(xiàn)在有-個(gè)UITableview與一個(gè)自定義Cell,,Cell中有一個(gè)按鈕,現(xiàn)在需求是可以訪問Cell與Cell中的按鈕,且分別讀出不同的內(nèi)容,如下圖所示意:

image.png

那么此時(shí)單純?cè)O(shè)置Cell為訪問元素然后再訪問子元素這個(gè)思路是行不通的,我們可以設(shè)置CelI不激活為isAccessibilityElement, 但是蘋果內(nèi)部是可以將Cell作為整個(gè)訪問元素且設(shè)置它的accessibilityL abel,完成button基本設(shè)置后, 再設(shè)置button的accessibilityHint就可以實(shí)現(xiàn)我們想要的效果。

核心代碼如下:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestCell *cell = [tableView dequeueReusableCellWithIdentifier:[TestCell description] forIndexPath:indexPath];
    [cell setDataWithVersionM:self.dataArray[indexPath.row]];
    cell.isAccessibilityElement = NO;
    return cell;
}

.....

- (void)setDataWithVersionM:(YBUserVersionM *)versionM {
  if ([versionM.versionCode isEqualToString:@"default"]) {
        self.accessibilityLabel = @"標(biāo)準(zhǔn)版";
    } else if ([versionM.versionCode isEqualToString:@"young"]) {
        self.accessibilityLabel = @"鄉(xiāng)村版";
    } else if ([versionM.versionCode isEqualToString:@"big"]) {
        self.accessibilityLabel = @"大字版";
    }
    NSString *chooseOrNotStr;
    if ([versionM.versionCode isEqualToString:[[YBVersionManager instance] getCurentVersion]]) {
        [self.chooseBtn setSelected:YES];
        chooseOrNotStr = @"已選中";
    } else {
        [self.chooseBtn setSelected:NO];
        chooseOrNotStr = @"未選中";
    }
    self.accessibilityHint = chooseOrNotStr;

}
6. 只有背景圖片無文字的控件.

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

image.png
self.backButton.accessibilityLabel = @"返回";
self.closeButton.accessibilityLabel = @"關(guān)閉";
self.closeButton.accessibilityTraits = UIAccessibilityTraitButton;
7.數(shù)字/電話號(hào)連讀處理,比如播報(bào)驗(yàn)證碼1234的時(shí)候,系統(tǒng)會(huì)自動(dòng)播報(bào)成“一千兩百三十四”。

① 在中間加分隔符 1:2:3:4,這樣就會(huì)一個(gè)數(shù)字一個(gè)數(shù)字的讀了;
② 轉(zhuǎn)換為對(duì)應(yīng)的大寫漢字即可;


self.cardNubLbl.accessibilityLabel = [YBVoiceOverTool acquireAccessableNumber:bankNum];


/// 將電話號(hào)碼轉(zhuǎn)換為旁白可讀的中文
+ (NSString *)acquireAccessableNumber:(NSString *)number {
    NSArray *transFromArr = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"0"];
    NSArray *transToArr = @[@"一",@"二",@"三",@"四",@"五",@"六",@"七",@"八",@"九",@"零"];
    NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:transToArr forKeys:transFromArr];
    NSMutableArray *resultArr = [NSMutableArray array];
    for (int i = 0; i < number.length; i++) {
        NSString *currentStr = [number substringWithRange:NSMakeRange(i, 1)];
        [resultArr addObject:dictionary[currentStr]?:currentStr];
    }
    NSString *resultStr = [resultArr componentsJoinedByString:@""];
    return resultStr;
}
8.調(diào)整頁面焦點(diǎn)順序
cell.shouldGroupAccessibilityChildren = YES;
cell.accessibilityElements = @[cell.leftBtn, cell.rightBtn];

注意: 調(diào)整層級(jí)時(shí)不要漏掉控件,否則旁白模式下這個(gè)被遺漏的控件會(huì)無法選中也無法播報(bào)。

9. 多音字讀錯(cuò)

解決方案:換位同音字替代。比如:“密碼重(chóng)置”,蘋果識(shí)別為“密碼重(zhòng)置”,可以用“密碼崇置” 來解決。accessibilityLabel 屬性僅僅旁白模式下朗讀,不影響展示。

self.tipsLbl.accessibilityLabel = @“密碼崇置”;
10.進(jìn)入語音界面,旁白音量超級(jí)小,幾乎聽不見

錄音要AVAudioSessionCategoryRecord模式,結(jié)束錄音設(shè)為AVAudioSessionCategoryPlayback

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
}


// 開始錄音
- (void)vprStartRecord {
    // sdk開始錄音
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    [[VoiceRecordManager sharedInstance] start];
}

// 結(jié)束錄音
- (void)vprStopRecordShowTips:(BOOL)isTips {
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    // sdk結(jié)束錄音
    [[VoiceRecordManager sharedInstance] stop];
....
}

注意:因?yàn)轫?xiàng)目中是進(jìn)入這個(gè)頁面旁白聲音就變小了,所以我在viewWillAppear 生命周期中也設(shè)置了setCategory:AVAudioSessionCategoryPlayback 模式。

11. 一段文案中有兩個(gè)跳轉(zhuǎn)事件:點(diǎn)擊隱私政策,只會(huì)跳轉(zhuǎn)第一個(gè),第二個(gè)一直跳轉(zhuǎn)無效

解決方案:
a、改成兩個(gè)控件,每個(gè)控件一個(gè)點(diǎn)擊事件;
b、如果是富文本的話,直接用\n 換行,變?yōu)閮尚姓故緟f(xié)議,可以分別跳轉(zhuǎn)兩個(gè)協(xié)議。

12. 支持卡片內(nèi)容三指滑動(dòng)
三指滑動(dòng).gif

核心代碼:

- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
    if (direction == UIAccessibilityScrollDirectionLeft) {
        if (self.selectIndex < 4 ) {
            self.selectIndex = self.selectIndex+1;            
            [self scrollToIndex:self.selectIndex];
            [self accessibilityScrollPage:self.selectIndex];
            return YES;
        }
    }else if (direction == UIAccessibilityScrollDirectionRight) {
        if (self.selectIndex != 0) {
            self.selectIndex = self.selectIndex-1;
            [self scrollToIndex:self.selectIndex];
            [self accessibilityScrollPage:self.selectIndex];
            return YES;
        }
    }
    return NO;
}

Demo示例下載

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容