iOS Accessibility易用性 VoiceOver(盲人模式)粗浅适配

1、打开盲人模式: 

手机(6s iOS12.4)路径:设置->通用->辅助功能(底部)->辅助功能快捷键->勾选旁白(voiceOver)bash


这里须要特别说明一下,在通用列表进入辅助功能列表页时,顶部也有个旁白选项能够打开该功能,可是这里最好仍是按照我图示的方式来打开旁白功能。由于正如红框框出来的那样,按照我这种方式打开旁白功能是能够直接快捷键(连按三下home键)开启关闭的,这在你的开发当中提高的效率可不是一点半点,由于盲人模式下的使用方式跟正常差距仍是比较大的,好比你要调试一个比较深层级的页面,能够先以正常方式进入到目的页面再按三下home键开启旁白。若是以另一种方式打开旁白,而没有开启快捷键的话,你必须以盲人方式进入目的页面,至关的蛋疼。app

2、属性解读

  • isAccessibilityElement 设置是否支持盲人模式 ,必须设置为YES系统才能聚焦到该元素。须要注意的是UIView是默认关闭的,UIButton、UILabel默认打开。
  • accessibilityLabel 用来描述控件是什么.UIButton和lable会默认从title和text中获取,textField从输入框的内容获取..当元素得到焦点时会第一个播放。
  • accessibilityTraits 元素的特征.如按钮,连接等.元素获取焦点后会在第二个播放.因为会播放按钮等,所以accessibilityLabel能够不用添加连接的描述,直接使用系统描述的特征,如:lable为登录,traint为按钮,系统会播放”登录->按钮”。
  • accessibilityHint 至关因而一个附加说明.第三个朗读,默认为nil。
  • accessibilityValue 元素的值.用在UISlider,UITextField等组件上.用来描述元素的值。
  • accessibilityFrame 元素的frame,当系统聚焦到当前元素时,会有一个黑色的外框,该值就是聚焦框的大小。当元素太小时能够经过设置该frame使得容易点击,这个不会改变app的UI.若是不想让系统读取到该元素,能够设置frame为CGRectZero。

3、特殊处理

  1. 若是是商品列表页,对应cell想要被聚焦后直接输出提示文案,只须要将本身设置为易用性元素,即self.isAccessibilityElement = YES;而后在能拿到业务数据的地方设置提示文案就好:self.accessibilityLabel = [NSString stringWithFormat:@"product name %@ %@",eventProduct.name,self.priceLbl.attributedText.string];
  2. 有的视图若是是本身绘制的话,像咱们App里面购物车底部的支付按钮,这是用UIBezierPath画出来的。这种处理就会稍微麻烦一点。须要实现一个定义在UIAccessibilityContainer.h  文件中的非正式协议,不须要用尖括号遵循协议,只用实现如下几个方法就行。虽然系统聚焦框跟按钮实际形状有点误差,可是影响不大。好在这种状况下,原来定义的点击事件也不须要特别处理。

    - (BOOL)isAccessibilityElement {    
        return NO;
    }     
    - (NSArray *)accessibilityElements {    
        return self.accessArray;
    }
    - (NSInteger)accessibilityElementCount {    
        return self.accessArray.count;
    }     
    
    @property (nonatomic, strong) NSArray           *accessArray;     
    - (NSArray *)accessArray {    
        if (_accessArray) {        
            return _accessArray;    
        }    
        UIAccessibilityElement *ele1 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self.bgView];    
        CGRect rect1 = CGRectMake(CGRectGetMinX(self.bgView.frame), CGRectGetMinY(self.bgView.frame), CGRectGetWidth(self.bgView.frame)/2.0, CGRectGetHeight(self.bgView.frame)); 
        ele1.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(rect1, self.bgView);    
        ele1.accessibilityLabel = @"PayPal";
        //这里最好加上这句,由于盲人用户听到这是按钮,才知道它可点击
        ele1.accessibilityTraits = UIAccessibilityTraitButton;        
    
        UIAccessibilityElement *ele2 = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];    
        CGRect rect2 = CGRectMake(CGRectGetMidX(self.bgView.frame), CGRectGetMinY(self.bgView.frame), CGRectGetWidth(self.bgView.frame)/2.0, CGRectGetHeight(self.bgView.frame));  
        ele2.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(rect2, self.bgView);    
        ele2.accessibilityLabel = PPString(SHOPPINGCART_PROCEEDTOCHECKOUT); 
        ele2.accessibilityTraits = UIAccessibilityTraitButton;       _accessArray = @[ele1, ele2];    
        return _accessArray;
    }复制代码



  3. 流程打断后的聚焦变化。当进入一个新界面后,系统默认会聚焦到左上角第一个元素,通常都是关闭按钮或者退出按钮。咱们业务流程规定,在用户结帐前,必须登陆才能执行后续流程。因此若是一个盲人游客状态下加购了商品到购物车,此时点击结帐。会先弹出登陆注册弹窗让其登陆。若是沿用系统的默认聚焦设定,将会很是不友好:由于用户先听到checkout button,点击后竟然又直接听到了close button,下意识确定会有点懵逼。因此这里咱们最好手动调整聚焦。处理方式:我直接将帐号输入栏的textField的accessibilityLabel设置为了@"Welcome, sign in or register to continue shopping.",而后在登陆注册控制器的viewDidAppear中调用方法UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.signInDataSource.emailTextField); 将界面聚焦到了帐号输入栏。ide

  4. 在一些特殊流程阶段,用户返回后数据会丢失,因此在其点击返回按钮时,会给一个确认弹窗,当前项目中不少确认弹窗都是咱们本身仿UIAlertView样式写的,而后添加在根window。因此这里也须要特别处理一下,不然弹窗出现之后靠左右扫的手势很难才能切换到咱们的自定义弹窗上。其实核心方法跟上面同样,自定义弹窗添加到根window之后调用UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, alertVew.titleLbl);方法将焦点聚焦到弹窗标题上。post


4、写在最后

这是我在盲人模式适配中的一点小经验,作以前在网上搜的资料比较少也比较老,因此想着碰到的问题记录一下,若是能恰巧帮助到碰到这个问题的朋友那就更好了。忽然要适配盲人模式的原由实际上是由于咱们的App和网站在美国被别人告了,说是不支持盲人使用,因此也只是大概适配了一下购物下单主流程,没有作得特别细。若是涉及到更细节深度的问题可能须要本身仔细阅读UIKit下的UIAccessibility.h文件了。网站


5、补充

流程切换致使的屏幕焦点切换,且须要加提示语音的问题。出如今咱们App的详情页,具体情景就是在详情页点击加购按钮的时候,会弹起一个半弹窗,让用户选择颜色、尺寸、数量等。ui


点击确认会加购成功,收起半弹窗并将焦点设置在详情页的加购按钮;同时产品要求出一个“add to cart successfully”的语音反馈。atom


那么如今的问题就是怎么样在屏幕焦点从半弹窗的加购按钮切换为详情页加购按钮的状况下(此时系统在读的提示为“Add to Cart ,Button”),加入这个“add to cart successfully”的语音反馈。一开始我找到的是UIAccessibilityConstants.h文件下的UIAccessibilityElementFocusedNotification ,至关于在屏幕焦点切换时会发出通知。在它的下面还有两个东西:
spa

// The corresponding value is the element that is now focused by the assistive technology.
UIKIT_EXTERN NSString *const UIAccessibilityFocusedElementKey NS_AVAILABLE_IOS(9_0);

// The corresponding value is the element that had previously been focused by the assistive technology.
UIKIT_EXTERN NSString *const UIAccessibilityUnfocusedElementKey NS_AVAILABLE_IOS(9_0);复制代码

看注释很容易想到他们三个是合在一块儿用的。因此个人解决方案就是详情页注册了UIAccessibilityElementFocusedNotification通知,在接收到通知的时候,用这两个key取到焦点切换先后的元素,若是符合条件的话,就延迟发送一个语音提示。主要代码以下:调试

//盲人模式下屏幕焦点切换通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(postAddToCartSuccessedVoiceTips:) name:UIAccessibilityElementFocusedNotification object:nil];

- (void)postAddToCartSuccessedVoiceTips:(NSNotification *)noti {
    UIButton *previous = noti.userInfo[UIAccessibilityUnfocusedElementKey];
    UIButton *current = noti.userInfo[UIAccessibilityFocusedElementKey];
    BOOL previousIsAddToCart = ISCLASS([UIButton class], previous) && ([previous.currentTitle isEqualToString:PPString(OK_STRING)] || [previous.currentTitle isEqualToString:PPString(PRODUCTDETAIL_ADDTOCART)]);
    BOOL currentIsAddToCart = ISCLASS([UIButton class], current) && [current.currentTitle isEqualToString:PPString(PRODUCTDETAIL_ADDTOCART)];
    if (previousIsAddToCart && currentIsAddToCart) {
        //判断屏幕焦点是从半弹窗的加购按钮 切换到的 详情页Add To cart按钮 ,须要提示加购成功语音
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, kAddToCartSuccessedTips);
        });
    }
}

复制代码

可是这种粗糙的方式会形成一个问题,若是在2.5秒的时间内系统没有读完“Add to Cart ,Button”这句话的时候,你后续发送的语音提示会打断正在朗读的提示。或者若是“Add to Cart ,Button”朗读速度比较快的话,要等零点几秒才能听到加购成功的提示。因此这是一种不够优雅的解决方式。若是有更好的解决方案,还请不吝赐教!code

相关文章
相关标签/搜索