我试图在UITableViewCell
中放置一个自定义视图,该视图当然位于UITableView
中。我想让这个自定义视图可以访问,所以我需要将它变成一个UIAccessibilityContainer
(因为它包含了几个没有作为自己的UIViews实现的可视元素)。
当我这样做时,每当表滚动时,元素的位置就会变得混乱。在使用VoiceOver分页浏览元素时,它将自动滚动表,试图在屏幕上对所选元素进行居中,但是VoiceOver认为元素的位置不再与其视觉位置一致的轮廓。
请注意,在屏幕截图中,检查人员说“第4行,元素2”,但是突出显示的区域是第7行中的一些随机区域,因为这恰好是第4行自动滚动表之前的位置。
我的想法是,当表视图滚动时,我可能不得不使用UIAccessibilityPostNotification()
发布布局更改,但当我不使用UIAccessibilityContainer
时,我不必这样做,而且感觉我不应该这样做,而系统应该为我处理这个问题--但是,UIAccessibilityElement
需要在屏幕坐标中设置它的accessibilityFrame
,这似乎给事情带来了麻烦。(额外的问题是:为什么API是这样设计的?为什么不定义相对于元素的容器或类似的框架呢?( Arg.)
这是自定义视图的实现,以防这里有什么东西会导致问题。对于整个项目(Xcode 4),单击此处。
@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
的变体,它的思想是,VoiceOver将在元素需要时以某种方式请求它,这将是更新内容的好时机。然而,这似乎也不起作用。我希望也许VoiceOver会自动要求重新绘制东西,但这似乎也不起作用。(将位置设置代码放在-drawRect:
中的想法来自于我在WWDC上看到的关于这个问题的东西,但我不清楚这是“最佳实践”还是刚好方便。)
发布于 2011-11-21 09:27:52
通过向可访问性方法添加一些副作用,并通过表滚动委托的协作,解决了您所描述的问题。在drawRect
方法中,我计算矩形的局部坐标,因此我不需要在那里转换坐标,只需计算它们相对于单元格的左上角。
然后,我修改了访问器以更新框架,其副作用如下(注意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;
}
虽然这对于初始视图很好,但在用户滚动时仍然会得到替换的框架,因此在表视图控制器中实现以下滚动委托:
- (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
查询每个单元格,以避免发送意外消息。
我还会在创建UIAccessibilityLayoutChangedNotification
对象的单元格设置器的末尾发布UIAccessibilityElement
,否则您将收到日志消息,说明您的元素消失或找不到。
这些变化使滚动的工作时,迭代元素之一与转子,但你可能会得到奇怪的结果,如果用户滚动一个三指手势。这是因为默认情况下,tableViews一次滚动一个屏幕页面,这个页面可能与单元格没有相同的元素边界,而转子选择的单元是半可见的。根据滚动方向和其他UI元素,半可见单元格可能重叠控制转子被混淆。您需要实现分页滚动以控制这种行为。
发布于 2011-05-04 14:05:12
这个问题会影响VoiceOver模式下应用程序的可用性吗?当我在Mac和iOS上玩过iOS时,突出显示框(尤其是在网页视图中)常常与其屏幕上的对象无法匹配。如果这个应用程序在VoiceOver中仍然可用,我会称它为已知的bug,并在有人抱怨时修复它。
毕竟,我认识的大多数盲人都不看高亮框。
https://stackoverflow.com/questions/5872453
复制相似问题