Swiftgram/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm

127 lines
4.6 KiB
Plaintext

/* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "_ASDisplayViewAccessiblity.h"
#import "_ASDisplayView.h"
#import "ASDisplayNodeExtras.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#pragma mark - UIAccessibilityElement
@implementation UIAccessibilityElement (_ASDisplayView)
+ (UIAccessibilityElement *)accessibilityElementWithContainer:(id)container node:(ASDisplayNode *)node
{
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:container];
accessibilityElement.accessibilityIdentifier = node.accessibilityIdentifier;
accessibilityElement.accessibilityLabel = node.accessibilityLabel;
accessibilityElement.accessibilityHint = node.accessibilityHint;
accessibilityElement.accessibilityValue = node.accessibilityValue;
accessibilityElement.accessibilityTraits = node.accessibilityTraits;
return accessibilityElement;
}
@end
#pragma mark - _ASDisplayView / UIAccessibilityContainer
static NSArray *ASCollectUIAccessibilityElementsForNode(ASDisplayNode *viewNode, ASDisplayNode *subnode, id container) {
NSMutableArray *accessibleElements = [NSMutableArray array];
ASDisplayNodePerformBlockOnEveryNodeBFS(subnode, ^(ASDisplayNode * _Nonnull currentNode) {
// For every subnode that is layer backed or it's supernode has shouldRasterizeDescendants enabled
// we have to create a UIAccessibilityElement as no view for this node exists
if (currentNode != viewNode && currentNode.isAccessibilityElement) {
UIAccessibilityElement *accessibilityElement = [UIAccessibilityElement accessibilityElementWithContainer:container node:currentNode];
// As the node hierarchy is flattened it's necessary to convert the frame for each subnode in the tree to the
// coordinate system of the supernode
CGRect frame = [viewNode convertRect:currentNode.bounds fromNode:currentNode];
accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(frame, container);
[accessibleElements addObject:accessibilityElement];
}
});
return [accessibleElements copy];
}
@interface _ASDisplayView () {
NSArray *_accessibleElements;
}
@end
@implementation _ASDisplayView (UIAccessibilityContainer)
#pragma mark - UIAccessibility
- (NSArray *)accessibleElements
{
ASDisplayNode *viewNode = self.asyncdisplaykit_node;
if (viewNode == nil) {
return nil;
}
// Handle rasterize case
if (viewNode.shouldRasterizeDescendants) {
_accessibleElements = ASCollectUIAccessibilityElementsForNode(viewNode, viewNode, self);
return _accessibleElements;
}
// Handle not rasterize case
NSMutableArray *accessibleElements = [NSMutableArray array];
for (ASDisplayNode *subnode in viewNode.subnodes) {
if (subnode.isAccessibilityElement) {
// An accessiblityElement can either be a UIView or a UIAccessibilityElement
id accessiblityElement = nil;
if (subnode.isLayerBacked) {
// No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node
accessiblityElement = [UIAccessibilityElement accessibilityElementWithContainer:self node:subnode];
} else {
accessiblityElement = subnode.view;
}
[accessiblityElement setAccessibilityFrame:UIAccessibilityConvertFrameToScreenCoordinates(subnode.frame, self)];
[accessibleElements addObject:accessiblityElement];
} else if (subnode.isLayerBacked) {
// Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement
[accessibleElements addObjectsFromArray:ASCollectUIAccessibilityElementsForNode(viewNode, subnode, self)];
} else if ([subnode accessibilityElementCount] > 0) {
// Add UIAccessibilityContainer
[accessibleElements addObject:subnode.view];
}
}
_accessibleElements = [accessibleElements copy];
return _accessibleElements;
}
- (NSInteger)accessibilityElementCount
{
return [self accessibleElements].count;
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
ASDisplayNodeAssertNotNil(_accessibleElements, @"At this point _accessibleElements should be created.");
if (_accessibleElements == nil) {
return nil;
}
return _accessibleElements[index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
if (_accessibleElements == nil) {
return NSNotFound;
}
return [_accessibleElements indexOfObject:element];
}
@end