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)的的事件有哪些