Add accessibility support for shouldRasterizeDescendants and layerBacked nodese

This commit is contained in:
Michael Schneider 2016-04-06 16:06:08 -07:00
parent 9c29d0efa8
commit 03a7c55d59

View File

@ -18,6 +18,8 @@
#import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import <queue>
@interface _ASDisplayView () @interface _ASDisplayView ()
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node; @property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@ -380,6 +382,22 @@
#pragma mark - Accessibility #pragma mark - Accessibility
static BOOL ASNodeValidForAccessibility(ASDisplayNode *node)
{
if (node.isAccessibilityElement) {
return YES;
}
if (node.isLayerBacked) {
// Assume for now that all nodes that have an accessibility label or
return node.accessibilityLabel.length > 0 ||
node.accessibilityValue.length > 0 ||
((node.accessibilityTraits & UIAccessibilityTraitNone) != UIAccessibilityTraitNone);
}
return YES;
}
static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
@implementation UIAccessibilityElement (_ASDisplayView) @implementation UIAccessibilityElement (_ASDisplayView)
@ -408,16 +426,50 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
- (NSArray *)accessibleElements - (NSArray *)accessibleElements
{ {
ASDisplayNode *asyncDisplayKitNode = self.asyncdisplaykit_node;
if ( _accessibleElements != nil ) { if ( _accessibleElements != nil ) {
return _accessibleElements; return _accessibleElements;
} }
_accessibleElements = [[NSMutableArray alloc] init]; _accessibleElements = [[NSMutableArray alloc] init];
ASDisplayNode *selfNode = self.asyncdisplaykit_node;
// Create UI accessiblity elements for each subnode that represent the subnode within the accessibility container // Handle rasterize case
for (ASDisplayNode *subnode in asyncDisplayKitNode.subnodes) { if (selfNode.shouldRasterizeDescendants) {
if (subnode.isAccessibilityElement || [subnode accessibilityElementCount] > 0) { // In this case we have to go through the whole subnodes tree in BFS fashion and create all
// accessibility elements ourselves as the view hierarchy is flattened
// Queue used to keep track of subnodes while traversing this layout in a BFS fashion.
std::queue<ASDisplayNode *> queue;
queue.push(selfNode);
while (!queue.empty()) {
ASDisplayNode *node = queue.front();
queue.pop();
// Check if we have to add the node to the accessiblity nodes as it's an accessiblity element
if (node != selfNode && ASNodeValidForAccessibility(node)) {
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
accessibilityElement.asyncdisplaykit_node = node;
[_accessibleElements addObject:accessibilityElement];
}
// Add all subnodes to process in next step
for (int i = 0; i < node.subnodes.count; i++)
queue.push(node.subnodes[i]);
}
return _accessibleElements;
}
// Handle not rasterize case
// Create UI accessiblity elements for each subnode that represent an elment within the accessibility container
for (ASDisplayNode *subnode in selfNode.subnodes) {
// Check if this subnode is a UIAccessibilityContainer
if (!subnode.isAccessibilityElement && [subnode accessibilityElementCount] > 0) {
// We are good and the view is an UIAccessibilityContainer so add that
[_accessibleElements addObject:subnode.view];
} else if (ASNodeValidForAccessibility(subnode)) {
// Create a accessiblity element from the subnode
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self]; UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
accessibilityElement.asyncdisplaykit_node = subnode; accessibilityElement.asyncdisplaykit_node = subnode;
[_accessibleElements addObject:accessibilityElement]; [_accessibleElements addObject:accessibilityElement];
@ -434,18 +486,28 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
- (id)accessibilityElementAtIndex:(NSInteger)index - (id)accessibilityElementAtIndex:(NSInteger)index
{ {
UIAccessibilityElement *element = [self accessibleElements][index]; UIAccessibilityElement *accessibilityElement = [[self accessibleElements] objectAtIndex:index];
ASDisplayNode *subnode = element.asyncdisplaykit_node; ASDisplayNode *accessibilityElementNode = accessibilityElement.asyncdisplaykit_node;
if (subnode == nil) { if (accessibilityElementNode == nil) {
return nil; return nil;
} }
// We have to update the accessiblity frame in accessibilityElementAtIndex: as the accessibility frame is in screen // We have to update the accessiblity frame in accessibilityElementAtIndex: as the accessibility frame is in screen
// coordinates and between creating the accessibilityElement and returning it in accessibilityElementAtIndex: // coordinates and between creating the accessibilityElement and returning it in accessibilityElementAtIndex:
// the frame can change // the frame can change
element.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(subnode.frame, self);
return element; // Handle if node is rasterized
ASDisplayNode *selfNode = self.asyncdisplaykit_node;
if (selfNode.shouldRasterizeDescendants) {
// We need to convert the accessibilityElementNode frame into the coordinate system of the selfNode
CGRect frame = [selfNode convertRect:accessibilityElementNode.bounds fromNode:accessibilityElementNode];
accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(frame, self);
return accessibilityElement;
}
// Handle non rasterized case
accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(accessibilityElementNode.frame, self);
return accessibilityElement;
} }
- (NSInteger)indexOfAccessibilityElement:(id)element - (NSInteger)indexOfAccessibilityElement:(id)element