39

我在屏幕上有一堆按钮,这些按钮在视觉上直观地定位,但 VoiceOver 并没有以直观的顺序阅读。这是因为某些按钮(例如向上和向下)位于彼此上方和下方。但是,画外音似乎开始从左到右,从上到下阅读。

这导致画外音在“向上”之后读取“向上”右侧的按钮,而不是在之后立即读取“向下”。

如何强制画外音阅读我想阅读的按钮?我应该提一下,我在画外音中使用了滑动到循环元素功能。

我所有的按钮都是 UIView 和 UIButton 的子类版本。这是我使用的按钮启动器的示例。忽略像素数 - 我知道这是不好的形式,但我现在处于紧要关头:

UIButton* createSpecialButton(CGRect frame, 
                                 NSString* imageName, 
                                 NSString* activeImageName,
                                 id target,
                                 SEL obClickHandler) 
{
    UIButton* b = [UIButton buttonWithType:UIButtonTypeCustom];
    [b setImage:[GlobalHelper nonCachedImage:imageName ofType:@"png"] 
       forState:UIControlStateNormal];
    [b setImage:[GlobalHelper nonCachedImage:activeImageName ofType:@"png"] 
       forState:UIControlStateHighlighted];
    [b addTarget:target action:obClickHandler forControlEvents:UIControlEventTouchUpInside];    
    b.frame= frame;
    return b;
}


- (UIButton *) createSendButton {
    CGFloat yMarker = 295;

    UIButton* b = createSpecialButton(CGRectMake(160, yMarker, 70, 45),
                                          @"Share_Btn",
                                          @"Share_Selected_Btn",
                                          self,
                                          @selector(sendAction));
    b.accessibilityHint = @"Send it!";
    b.accessibilityLabel = @"Stuff for voiceover to be added";
    [self.view addSubview:b];

    return b;
}
4

9 回答 9

53

您可以更改设置视图的 accessibilityElements 数组的顺序:

self.view.accessibilityElements = @[self.view1, self.view2, self.view3, self.view4];

或者

self.anotherView.accessibilityElements = @[self.label1, self.txtView1, self.label2, self.txtView2];

如果您需要以编程方式设置启用交互:

[self.view1 setUserInteractionEnabled:YES];

如果视图被隐藏,则配音将不会通过它。

于 2015-07-21T20:01:17.147 回答
30

最简单的答案是创建一个UIView包含按钮的子类,并对来自系统的可访问性调用做出不同的响应。这些重要的电话是:

-(NSInteger)accessibilityElementCount
-(id)accessibilityElementAtIndex:
-(NSInteger)indexOfAccessibilityElement:

我已经看过其中一些问题,并且之前回答过一个问题,但我还没有看到如何重新排序 VoiceOver 焦点的通用示例。因此,这里有一个示例,说明如何创建一个UIView子类,通过标签将其可访问的子视图公开给 VoiceOver。

AccessibilitySubviewsOrderedByTag.h

#import <UIKit/UIKit.h>
@interface AccessibilitySubviewsOrderedByTag : UIView
@end

AccessibilitySubviewsOrderedByTag.m

#import "AccessibilityDirectional.h"
@implementation AccessibilitySubviewsOrderedByTag {
    NSMutableArray *_accessibilityElements;
}
    //Lazy loading accessor, avoids instantiating in initWithCoder, initWithFrame, or init.
-(NSMutableArray *)accessibilityElements{
    if (!_accessibilityElements){
        _accessibilityElements = [[NSMutableArray alloc] init];
    }
    return _accessibilityElements;
}
// Required accessibility methods...
-(BOOL)isAccessibilityElement{
    return NO;
}
-(NSInteger)accessibilityElementCount{
    return [self accessibilityElements].count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
    return [[self accessibilityElements] objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
    return [[self accessibilityElements] indexOfObject:element];
}
// Handle added and removed subviews...
-(void)didAddSubview:(UIView *)subview{
    [super didAddSubview:subview];
    if ([subview isAccessibilityElement]){
        // if the new subview is an accessibility element add it to the array and then sort the array.
        NSMutableArray *accessibilityElements = [self accessibilityElements];
        [accessibilityElements addObject:subview];
        [accessibilityElements sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
            // Here we'll sort using the tag, but really any sort is possible.
            NSInteger one = [(UIView *)obj1 tag];
            NSInteger two = [(UIView *)obj2 tag];
            if (one < two) return NSOrderedAscending;
            if (one > two) return NSOrderedDescending;
            return NSOrderedSame;
        }];
    }
}
-(void)willRemoveSubview:(UIView *)subview{
    [super willRemoveSubview:subview];
    // Clean up the array. No check since removeObject: is a safe call.
    [[self accessibilityElements] removeObject:subview];
}
@end

现在只需将您的按钮包含在此视图的一个实例中,并将按钮上的标记属性设置为本质上是焦点顺序。

于 2012-11-15T22:41:08.277 回答
14

在 Swift 中你只需要设置视图的accessiblityElements数组属性:

view.accessibilityElements = [view1, view2, view3]// 你想要的订单

于 2017-07-18T07:03:18.730 回答
5

我知道这是一个旧线程,但我发现最简单的方法是将 UIView 子类化(Swift 3)。然后只需将情节提要中的主要 UIView 类型修改为AccessibiltySubviewsOrderedByTag并更新您要按顺序阅读的每个子视图中的标签。

class AccessibilitySubviewsOrderedByTag: UIView {
    
    override func layoutSubviews() {
        
        self.accessibilityElements = [UIView]()
        for accessibilitySubview in self.subviews {
            if accessibilitySubview.isAccessibilityElement {
                self.accessibilityElements?.append(accessibilitySubview)
            }
        }
        self.accessibilityElements?.sort(by: {($0 as AnyObject).tag < ($1 as AnyObject).tag})
    }
}
于 2018-02-09T16:27:04.063 回答
4

这并没有直接回答原始问题,但它回答了问题的标题:

当我希望 VoiceOver 向下滑动列时,我一直在使用包含视图来shouldGroupAccessibilityChildren设置列。

我希望我早点知道这一点,因为将容器追溯插入自动布局情况可能会很痛苦……</p>

于 2017-08-12T17:10:09.097 回答
3

我尝试了韦斯利设置数组的答案,accessibilityElements但它对我不起作用。

Apple 有一些文档Enhancing the Accessibility of Table View Cells with an example in code。基本上,您将单元格(父视图)的可访问性标签设置为子视图的可访问性标签的值。

[cell setAccessibilityLabel:[NSString stringWithFormat:@"%@, %@", cityLabel, temperatureLabel]];

这对我有用。

于 2015-12-22T18:44:05.240 回答
1

我认为你可以在故事板中做到这一点。VoiceOver 顺序由文档大纲中视图的顺序决定。

只需以正确的顺序拖放视图层次结构中的视图即可。

编辑:

对不起,我不能发布截图,直到 10 声望。在情节提要中,文档大纲是左侧列出您的场景及其子视图的区域。在这里,子视图按顺序排列在彼此的下方。当您更改此顺序时,VoiceOver 的阅读顺序将发生变化。

于 2015-02-16T13:06:43.920 回答
1

Swift5 你可以设置视图的 accessiblityElements 数组属性:
当父视图是 UIStackView 时也有效

view.accessibilityElements = [view1, label2, button3...]// 你想要的订单

于 2021-06-01T08:01:44.083 回答
0

我昨天找到了一个方便的方法。类似于@TejAces 的回答。制作一个新的 swift 文件,然后将这些内容复制到其中。

import UIKit

extension UIView {
    func updateOrder(_ direction: Bool = true) {
        var tempElements: [Any]? = [Any]()
        let views = (direction) ? subviews : subviews.reversed()
        for aView in views {
            tempElements?.append(aView)
        }
        accessibilityElements = tempElements
    }
}

class ReorderAccessibilityByStoryBoardView: UIView {
    override func didAddSubview(_ subview: UIView) {
        updateOrder()
        super.didAddSubview(subview)
    }
}

将 UIView(包含要重新排序的视图)的类设置为 ReorderAccessibilityByStoryBoardView。然后您可以通过重新排序故事板的视图列表来重新排序它们。

查看列表

因为子视图不包含StackView/ScrollView中的视图,所以需要在这个文件中创建一个独立的类。比如下面的 ReorderAccessibilityByStoryBoardStackView。

class ReorderAccessibilityByStoryBoardStackView: UIStackView {
    override func didAddSubview(_ subview: UIView) {
        updateOrder(false)
        super.didAddSubview(subview)
    }
}

使用这些代码,您还可以通过按特定顺序添加视图来重新排序代码中添加的视图。

于 2018-09-02T07:41:42.330 回答