Thanks for your previous input on carousel accessibility. We have a few follow-up questions:
Adjustable Trait Clarification: Thanks for the clarification regarding accessibilityCustomActions for navigation; I believe we're aligned on that front. Is there guidance on using .adjustable for a carousel depending on whether cards have single vs. multiple actionable elements? Or is the recommendation to avoid .adjustable for carousels altogether?
Skipping Entire Carousel:
Our research has found, particularly for carousels with many items, it can be inefficient for users to navigate linearly through the entire carousel's contents in order to reach UI elements that appear after it. If content inside the carousel should be focusable with standard navigation gestures, is there a recommended method for VoiceOver users to skip an entire carousel and navigate to the next element?
VoiceOver's documentation refers to a gesture that allows users to "Move out of a group of items", which seems like it might be relevant here. Would this concept of group navigation apply in the case of carousels? If so, is there developer documentation covering how we can support this?
VoiceOver Focus on Three-Finger Swipe (Pagination): We tried overriding accessibilityScroll(_:) as it seems scrolling the carousel does not cause VoiceOver focus to move, even if the previously focused element goes off screen. Would we be expected to manage this manually, e.g. with UIAccessibilityPostNotification, after the carousel scrolls to the next or previous element? For context, here's a simplified version of the prototype I wrote after you suggested using accessibilityScroll:
private func currentlyFocusedElementIndex() -> Int? {
guard let focusedElement
= UIAccessibility.focusedElement(using: .notificationVoiceOver)
as? AnyObject
else {
return nil
}
// Return the index of the currently focused carousel element. If VoiceOver
// focus is not within the carousel, return nil.
//
// In a real app, this would be more complicated, because we have multiple
// elements per card, and we want to jump between cards, not individual
// elements. But the structure would be the same.
return self.cards.index { $0 === focusedElement }
}
// accessibilityScroll(_:) calls this method
private func handleAccessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
guard let currentIndex = currentlyFocusedElementIndex() else {
return false
}
if direction == .right {
guard currentIndex.cardIndex > 0 else {
return false
}
self.scrollToIndex(currentIndex - 1, animated: true)
return true
} else if direction == .left {
guard currentIndex.cardIndex < self.cards.count - 1 else {
return false
}
self.scrollToIndex(currentIndex + 1, animated: true)
return true
}
return false
}
private func scrollToIndex(_ index: CarouselItemIndex, animated: Bool) {
let newFrame = self.cards[index.cardIndex].view.frame
self.carouselView.scrollRectToVisible(newFrame, animated: animated)
UIAccessibility.post(notification: .layoutChanged,
argument: self.cards[index])
}
Topic:
Accessibility & Inclusion
SubTopic:
General
Tags: