iOS手機(jī)無障礙編程總結(jié)

VoiceOver簡(jiǎn)介

VoiceOver是蘋果“讀屏”技術(shù)的名稱,屬于輔助功能的一部分换可。VoiceOver可以讀出屏幕上的信息椎椰,以幫助盲人進(jìn)行人機(jī)交互。 這項(xiàng)技術(shù)在蘋果的各個(gè)系統(tǒng)中都可以看到沾鳄,OS X慨飘,iOS,watchOS,甚至tvOS瓤的。

無障礙編程思路

平常要是我們用蘋果原生的控件的話休弃,那基本上是天生自帶VoiceOver的,也就是所有的東西都配合的天衣無縫圈膏,但是呢塔猾,要是某些小按鈕啊,神馬的稽坤,我們沒有妥妥的設(shè)置好label之類的東西丈甸,更甚者用image之類的東西來當(dāng)按鈕的,項(xiàng)目跑起來VoiceOver就不好用了

無障礙開發(fā)實(shí)現(xiàn)細(xì)節(jié)

1.針對(duì)于原生控件

原生界面基本的UI元素可以設(shè)置一些屬性來改變VoiceOver的效果:

  • isAccessibilityElement 該UI元素是否支持無障礙輔助功能
  • accessibilityLabel告訴用戶這個(gè)是什么
  • accessibilityTraits告訴用戶這是什么控件
  • accessibilityValue 告訴用戶這個(gè)控件的值是什么尿褪,常用在文本編輯或者可以修改的東西的地方
  • accessibilityHint告訴用戶睦擂,這是干啥用的
實(shí)例代碼如下:
self.editButtonItem.isAccessibilityElement = YES;
self.editButtonItem.accessibilityLabel = @"編輯";
self.editButtonItem.accessibilityTraits = UIAccessibilityTraitButton;
self.editButtonItem.accessibilityHint = @"編輯表格中的數(shù)據(jù)";
2.自定義UIView上的子視圖

在自定義view中有時(shí)包含了一些非標(biāo)準(zhǔn)控件或者非UIView子類的可觸摸UI原素,比如通過draw方法畫出來的區(qū)域,則以上的兩種情況都不能實(shí)現(xiàn)無障礙體驗(yàn),這種情況下,則需要實(shí)現(xiàn)UIAccessibilityContainer Protocol來實(shí)現(xiàn).UIAccessibilityContainer Protocol是非正式協(xié)議。

具體實(shí)現(xiàn)步驟:
  • 定義一個(gè)NSMutableArray屬性 用于保存所有的accessible Elements
  • 創(chuàng)建多個(gè)UIAccessibilityElement(根據(jù)子視圖數(shù)量或需求)杖玲,指定相關(guān)的屬性顿仇,然后添加到數(shù)組中
  • 實(shí)現(xiàn)- (NSInteger)accessibilityElementCount代理方法,指定支持無障礙體驗(yàn)的子視圖數(shù)量
  • 實(shí)現(xiàn)- (BOOL)isAccessibilityElement,指定父視圖是否支持無障礙體驗(yàn)摆马,父View如果為AccessibilityElement,子element將不響應(yīng)VoiceOver,所以這里要返回NO
  • 實(shí)現(xiàn)- (id)accessibilityElementAtIndex:(NSInteger)index,返回對(duì)應(yīng)index下的UIAccessibilityElement
  • 實(shí)現(xiàn)- (NSInteger)indexOfAccessibilityElement:(id)element,反對(duì)對(duì)應(yīng)UIAccessibilityElement的index
實(shí)例代碼如下:
#import "LogoView.h"

@interface LogoView ()

@property (nonatomic, strong)NSMutableArray *accessibleElements;
@property (weak, nonatomic) IBOutlet UIImageView *img_1;
@property (weak, nonatomic) IBOutlet UIImageView *img_2;
@property (weak, nonatomic) IBOutlet UIImageView *img_3;
@property (weak, nonatomic) IBOutlet UILabel *title_1;
@property (weak, nonatomic) IBOutlet UILabel *title_2;
@property (weak, nonatomic) IBOutlet UILabel *title_3;

@end

@implementation LogoView

+(LogoView *)loadLogoView {
    return [[NSBundle mainBundle] loadNibNamed:@"LogoView" owner:nil options:nil].lastObject;
}

- (NSArray *)getAccessElement {
    if (_accessibleElements) {
        return _accessibleElements;
    }
    _accessibleElements = [[NSMutableArray alloc] init];

    UIAccessibilityElement *element_1 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_1.isAccessibilityElement = YES;
    element_1.accessibilityFrame = [self convertRect:self.img_1.frame toView:nil];
    element_1.accessibilityLabel = NSLocalizedString(@"游戲中心", nil);
    element_1.accessibilityTraits = UIAccessibilityTraitImage;
    [_accessibleElements addObject:element_1];
    
    UIAccessibilityElement *element_2 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_2.isAccessibilityElement = YES;
    element_2.accessibilityFrame = [self convertRect:self.img_2.frame toView:nil];
    element_2.accessibilityLabel = NSLocalizedString(@"全區(qū)排行", nil);
    element_2.accessibilityTraits = UIAccessibilityTraitImage;
    [_accessibleElements addObject:element_2];
    
    UIAccessibilityElement *element_3 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_3.isAccessibilityElement = YES;
    element_3.accessibilityFrame = [self convertRect:self.img_3.frame toView:nil];
    element_3.accessibilityLabel = NSLocalizedString(@"原創(chuàng)排行", nil);
    element_3.accessibilityTraits = UIAccessibilityTraitImage;
    [_accessibleElements addObject:element_3];
    
    UIAccessibilityElement *element_4 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_4.isAccessibilityElement = YES;
    element_4.accessibilityFrame = [self convertRect:self.title_1.frame toView:nil];
    element_4.accessibilityLabel = NSLocalizedString(@"游戲中心", nil);
    element_4.accessibilityTraits = UIAccessibilityTraitStaticText;
    [_accessibleElements addObject:element_4];
    
    UIAccessibilityElement *element_5 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_5.isAccessibilityElement = YES;
    element_5.accessibilityFrame = [self convertRect:self.title_2.frame toView:nil];
    element_5.accessibilityLabel = NSLocalizedString(@"全區(qū)排行", nil);
    element_5.accessibilityTraits = UIAccessibilityTraitStaticText;
    [_accessibleElements addObject:element_5];
    
    UIAccessibilityElement *element_6 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
    element_6.isAccessibilityElement = YES;
    element_6.accessibilityFrame = [self convertRect:self.title_3.frame toView:nil];
    element_6.accessibilityLabel = NSLocalizedString(@"原創(chuàng)排行", nil);
    element_6.accessibilityTraits = UIAccessibilityTraitStaticText;
    [_accessibleElements addObject:element_6];
    
    return _accessibleElements;
}

- (NSInteger)accessibilityElementCount {
    return [self getAccessElement].count;
}

- (BOOL)isAccessibilityElement {
    return NO;
}

- (id)accessibilityElementAtIndex:(NSInteger)index {
    return [[self getAccessElement] objectAtIndex:index];
}

- (NSInteger)indexOfAccessibilityElement:(id)element {
    return [[self getAccessElement] indexOfObject:element];
}

@end
3.表格中復(fù)雜的cell

表格cell中臼闻,如果子視圖比較多且復(fù)雜的時(shí)候或者除文本視圖以外包含圖片按鈕等子控件的時(shí)候,無障礙信息可能會(huì)出現(xiàn)播報(bào)內(nèi)容不全的情況今膊,需要手動(dòng)修改

具體實(shí)現(xiàn)步驟:
  • 在自定義cell中些阅,出現(xiàn)特殊的子視圖伞剑,如圖片斑唬、按鈕等,需要將特殊的視圖設(shè)置成無障礙元素黎泣,并且設(shè)置對(duì)應(yīng)的提示類型等相關(guān)屬性
  • 重寫- (NSString *)accessibilityLabel方法恕刘,然后將對(duì)應(yīng)的控件轉(zhuǎn)成字符串拼接到一起,語音會(huì)按照最先拼接的字符串讀出來
實(shí)例代碼如下:
- (NSString *)accessibilityLabel {
    
    NSString *scenery = [self.sceneryImg accessibilityLabel];
    NSString *nameLabel = [self.name accessibilityLabel];
    NSString *sexLabel = [self.sex accessibilityLabel];
    NSString *ageLabel = [self.age accessibilityLabel];
    NSString *delBtn = [self.deleteBtn accessibilityLabel];
    
    return [NSString stringWithFormat:@"%@, %@, %@, %@, %@", scenery, nameLabel, sexLabel, ageLabel, delBtn];
}
4.列表滑動(dòng)

表格在VoiceOver開啟狀態(tài)下抒倚,三個(gè)手指上下滑動(dòng)的時(shí)候褐着,語音默認(rèn)是中英文混合提示,有一定的偏差托呕,可以進(jìn)行手動(dòng)設(shè)置來修改相關(guān)語音提示

具體實(shí)現(xiàn)步驟:
  • 重寫- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction方法含蓉,參數(shù)列表中的參數(shù)是個(gè)枚舉,可以判斷出向上或者向下项郊,然后算出表格的總行數(shù)馅扣,滑動(dòng)的行數(shù),修改改成指定的語音着降,語音提示會(huì)覆蓋掉默認(rèn)的語音提示
實(shí)例代碼如下:
- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
{
    BOOL isScrollingUp = NO;
    switch (direction) {
        case UIAccessibilityScrollDirectionUp: {
            isScrollingUp = YES;
        } break;
            
        case UIAccessibilityScrollDirectionDown: {
            isScrollingUp = NO;
        } break;
            
        default:
            // These cases are not handled
            return NO;
    }
    
    NSInteger numberOfCellsToScroll = 1; // Any number you'd like
    
    NSInteger newRow = -1;
    if (isScrollingUp) {
        newRow = [self.tableView indexPathsForVisibleRows][0].row - numberOfCellsToScroll;
    } else {
        newRow = [[self.tableView indexPathsForVisibleRows] lastObject].row + numberOfCellsToScroll;
    }
    NSLog(@"######newRow %ld", newRow);
    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:newRow inSection:0]
                          atScrollPosition:UITableViewScrollPositionMiddle
                                  animated:YES];
    
    UIAccessibilityPostNotification(UIAccessibilityPageScrolledNotification,
                                    [NSString stringWithFormat:@"當(dāng)前滑到了第%ld行", newRow]);
    
    return YES; // We handled the scroll
}

總結(jié)

簡(jiǎn)單點(diǎn)來說在App開發(fā)過程中關(guān)于VoiceOver我們需要關(guān)注如下幾點(diǎn):

  • 界面上的AccessibilityElement有哪些
  • AccessibilityElement的位置和形狀
  • AccessibilityElement的信息是什么(就是Element被選中后差油,被讀出來內(nèi)容)
  • AccessibilityElement所能響應(yīng)的的事件有哪些
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蓄喇,更是在濱河造成了極大的恐慌发侵,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妆偏,死亡現(xiàn)場(chǎng)離奇詭異刃鳄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)楼眷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門铲汪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人罐柳,你說我怎么就攤上這事掌腰。” “怎么了张吉?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵齿梁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我肮蛹,道長(zhǎng)勺择,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任伦忠,我火速辦了婚禮省核,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昆码。我一直安慰自己气忠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布赋咽。 她就那樣靜靜地躺著旧噪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脓匿。 梳的紋絲不亂的頭發(fā)上淘钟,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音陪毡,去河邊找鬼米母。 笑死,一個(gè)胖子當(dāng)著我的面吹牛毡琉,可吹牛的內(nèi)容都是我干的铁瞒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绊起,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼精拟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤蜂绎,失蹤者是張志新(化名)和其女友劉穎栅表,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體师枣,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怪瓶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了践美。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洗贰。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陨倡,靈堂內(nèi)的尸體忽然破棺而出敛滋,到底是詐尸還是另有隱情,我是刑警寧澤兴革,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布绎晃,位于F島的核電站,受9級(jí)特大地震影響杂曲,放射性物質(zhì)發(fā)生泄漏庶艾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一擎勘、第九天 我趴在偏房一處隱蔽的房頂上張望咱揍。 院中可真熱鬧,春花似錦棚饵、人聲如沸煤裙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽积暖。三九已至藤为,卻和暖如春怪与,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缅疟。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工分别, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人存淫。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓耘斩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親桅咆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子括授,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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