UIAccessibilityContainer в табличном представлении - PullRequest
4 голосов
/ 03 мая 2011

Я пытаюсь поместить пользовательский вид в UITableViewCell, который, конечно, живет в пределах UITableView. Я хочу сделать этот пользовательский вид доступным, поэтому мне нужно сделать его UIAccessibilityContainer (так как он содержит несколько визуальных элементов, которые не реализованы как их собственные UIViews).

Когда я делаю это, расположение элементов запутывается всякий раз, когда таблица прокручивается. При перемещении по элементам с помощью VoiceOver он автоматически прокручивает таблицу, пытаясь отцентрировать выбранный элемент на экране, но затем контур того, где VoiceOver считает, что элемент больше не совпадает с тем, где он находится визуально.

broken

Обратите внимание, что на снимке экрана инспектор говорит: «Строка 4, элемент 2», но выделенная область - это случайное место в строке 7, поскольку это происходит там, где строка 4 находилась до автоматической прокрутки таблицы.

Я думаю, что мне, возможно, придется использовать UIAccessibilityPostNotification(), чтобы опубликовать изменение макета, когда прокручивается табличное представление, но мне не нужно это делать, когда я не использую UIAccessibilityContainer, и мне кажется, что я не должен был это делать, и что система должна обрабатывать это для меня - но тот факт, что UIAccessibilityElement должен иметь его accessibilityFrame, установленный в экранных координатах, кажется, бросает складки на вещи. (Дополнительный вопрос: почему, черт возьми, API-интерфейс спроектирован таким образом? Почему бы не определить фрейм относительно контейнера элемента или что-то в этом роде? Арг.)

Вот реализация пользовательского представления на случай, если здесь есть что-то, что вызывает проблему. Для полного проекта (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


Edit : я должен заметить, что я пробовал варианты, которые обновляют accessibilityFrame элемента в -indexOfAccessibilityElement: и -accessibilityElementAtIndex: с идеей, что VoiceOver будет запрашивать элемент каким-либо образом, когда ему это нужно, и это быть хорошим временем, чтобы обновить вещи. Однако это тоже не работает. Я надеялся, что VoiceOver автоматически запросит что-то перерисовать, но это тоже не работает. (Идея поместить код установки местоположения в -drawRect: исходит из того, что я помню, когда видел на WWDC об этом, но мне было неясно, было ли это «наилучшей практикой» или просто оказалось удобным.)

Ответы [ 2 ]

1 голос
/ 21 ноября 2011

Я решил проблему, которую вы описали, добавив некоторые побочные эффекты в методы доступности и совместную работу делегатов с прокруткой таблицы.Внутри метода 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, или выполучить сообщения журнала о том, что ваши элементы исчезли или не могут быть найдены.

Эти изменения заставляют прокрутку работать при итерации элементаs один за другим с ротором, но вы можете получить все еще странные результаты, если пользователь прокручивает с жестом тройного пальца.Это происходит потому, что по умолчанию tableViews прокручивают экранную страницу за раз, что может не совпадать с границами элементов, как у ваших ячеек, и ротор выбирает ячейку, видимую наполовину.В зависимости от направления прокрутки и других элементов пользовательского интерфейса, половина видимой ячейки может перекрывать элементы управления, с которыми спутан ротор.Вам необходимо реализовать прокрутку с постраничной ориентацией, чтобы избежать такого поведения.

0 голосов
/ 04 мая 2011

Влияет ли эта проблема на удобство использования приложения в режиме VoiceOver? Когда я играл с VoiceOver как на Mac OS, так и на iOS, поля подсветки (особенно в веб-представлениях) часто не совпадают с их экранными объектами. Если приложение все еще можно использовать в VoiceOver, я бы назвал это известной ошибкой и исправил бы ее, если кто-то пожалуется.

В конце концов, большинство слепых людей, которых я знаю, не смотрят на выделенные рамки.

...