iphone - TableView 中的 UIAccessibilityContainer

标签 iphone objective-c cocoa-touch uikit accessibility

我正在尝试将自定义 View 放入 UITableViewCell 中,它当然位于 UITableView 中。我想让这个自定义 View 可访问,所以我需要将它设为 UIAccessibilityContainer(因为它包含几个未作为自己的 UIView 实现的视觉元素)。

当我这样做时,只要表格滚动,元素的位置就会变得一团糟。使用 VoiceOver 对元素进行分页时,它会自动滚动表格以尝试将所选元素在屏幕上居中,但随后 VoiceOver 认为该元素所在位置的轮廓不再与它在视觉上的位置对齐。

broken

请注意,在屏幕截图中,检查器显示“第 4 行,元素 2”,但突出显示的区域是第 7 行中的某个随机位置,因为它恰好是第 4 行在自动滚动表格之前的位置。

我的想法是,当表格 View 滚动时,我可能必须使用 UIAccessibilityPostNotification() 来发布布局更改,但是当我不使用 UIAccessibilityContainer 感觉我不应该这样做,系统应该为我处理这个 - 但 UIAccessibilityElement 需要它的事实是 accessibilityFrame 在屏幕坐标中设置似乎确实对事物产生了影响。 (额外的问题:API 到底为什么要那样设计?为什么不相对于元素的容器或类似的东西定义框架?Arg.)

这是自定义 View 的实现,以防万一这里有什么东西导致了问题。对于完整项目 (Xcode 4),click here

@implementation CellView
@synthesize row=_row;

- (void)dealloc
{
    [_accessibleElements release];
    [super dealloc];
}

- (void)setRow:(NSInteger)newRow
{
    _row = newRow;

    [_accessibleElements release];
    _accessibleElements = [[NSMutableArray arrayWithCapacity:0] retain];

    for (NSInteger i=0; i<=_row; i++) {
        UIAccessibilityElement *element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
        element.accessibilityValue = [NSString stringWithFormat:@"Row %d, element %d", _row, i];
        [_accessibleElements addObject:element];
        [element release];
    }    

    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    [[UIColor lightGrayColor] setFill];
    UIRectFill(self.bounds);

    [[UIColor blackColor] setFill];
    NSString *info = [NSString stringWithFormat:@"Row: %d", _row];
    [info drawAtPoint:CGPointZero withFont:[UIFont systemFontOfSize:12]];

    [[[UIColor whiteColor] colorWithAlphaComponent:0.5] setFill];
    NSInteger x=0, y=0;
    for (NSInteger i=0; i<=_row; i++) {
        CGRect rect = CGRectMake(12+x, 22+y, 30, 30);

        UIAccessibilityElement *element = [_accessibleElements objectAtIndex:i];
        element.accessibilityFrame = [self.window convertRect:[self convertRect:rect toView:self.window] toWindow:nil];

        UIRectFill(rect);
        x += 44;

        if (x >= 300) {
            x = 0;
            y += 37;
        }
    }
}

- (BOOL)isAccessibilityElement
{
    return NO;
}

- (NSInteger)accessibilityElementCount
{
    return [_accessibleElements count];
}

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

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

@end


编辑:我应该注意到,我已经尝试过在 -indexOfAccessibilityElement:-accessibilityElementAtIndex: 中更新元素的 accessibilityFrame 的变体: code> 的想法是 VoiceOver 会在需要时以某种方式请求该元素,这将是更新内容的好时机。然而,这似乎也不起作用。我有点希望 VoiceOver 会自动请求重绘,但这似乎也行不通。 (将位置设置代码放在 -drawRect: 中的想法来 self 记得在 WWDC 上看到的关于此的内容,但我不清楚这是“最佳实践”还是恰好方便.)

最佳答案

我已经通过向可访问性方法添加一些副作用并与表格滚动委托(delegate)协作解决了您描述的问题。在 drawRect 方法中,我计算矩形的 local 坐标,因此我不需要在那里转换坐标,只需根据单元格的左上角计算它们.

然后,我修改了访问器以使用这样的副作用更新框架(注意 y 重置):

- (id)accessibilityElementAtIndex:(NSInteger)index
{
    UIAccessibilityElement *element = [accesible_items_ get:index];
    CGRect rect = element.accessibilityFrame;
    rect.origin.y = 0;
    element.accessibilityFrame = [self.window
        convertRect:rect fromView:self];
    return element;
}

虽然这对于初始 View 工作正常,但当用户滚动时您仍然会得到位移帧,因此在您的 TableView Controller 中实现以下滚动委托(delegate):

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // This loop has a side effects, see the cell accesor code.
    for (id cell in self.tableView.visibleCells)
        for (int f = 0; [cell accessibilityElementAtIndex:f]; f++);

    UIAccessibilityPostNotification(
        UIAccessibilityLayoutChangedNotification, nil);
    NSLog(@"Layout changed after scrollViewDidScroll");
}

根据表格的内容,并非所有单元格都可以响应可访问性方法,因此您可以先使用 respondsToSelector 查询每个单元格,以避免发送意外消息。

我还会在创建 UIAccessibilityElement 对象的单元格 setter 末尾发布 UIAccessibilityLayoutChangedNotification,否则您将收到日志消息,说明您的元素消失或找不到.

这些更改使滚动在使用转子一个接一个地迭代元素时起作用,但如果用户使用三指手势滚动,您可能仍然会得到奇怪的结果。这是因为默认情况下,tableViews 一次滚动一个屏幕页面,这可能不会恰好与您的单元格具有相同的元素边界,并且转子会选择一个半可见的单元格。根据滚动方向和其他 UI 元素,半个可见单元格可能会重叠控制转子混淆。您需要实现分页滚动来控制避免这种行为。

关于iphone - TableView 中的 UIAccessibilityContainer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5872453/

相关文章:

iphone - UITableView 行高崩溃

iphone - 如何让 UIPickerView 在屏幕上滑动一半?

ios - 如何重置 Xcode 6 中的导航 Controller ?

ios - 不使用桥接 header 访问私有(private) UIKit 函数

iPhone崩溃记者

ios - 中心对齐蒙版 UIImageView/模仿 Twitter 和 Facebook 应用程序的 ImageView

iphone - IOS:如何为缩进级别的变化设置动画?

iphone - 使用 C++ 和 sqlite 从 call_history.db 中提取一个表

ios - 使用 sections 将 indexPath 减一

objective-c - 要子类化什么类 - NSView 或 NSButton