From 7d21ccc184fb570f7527060cb8d61fa856e9eb8d Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Tue, 16 Aug 2016 17:01:06 -0700 Subject: [PATCH] [_ASDisplayViewAccessiblity] Fix accessibility in scroll views (#2079) * Fixes issues with accessibility elements in scroll views There are two changes here: 1. Because we can't reliably update the screen positions of accessibility elements contained within a scroll view, we need to calculate them on demand, so we override the accessibilityFrame method to do that. 2. Throwing away our calculated accessibility elements on frame change seemed to cause the accessibility system to be confused. Combining these two fixes together results in success, yay! * Don't set the accessibilityFrame, getter is overridden. --- .../Details/_ASDisplayViewAccessiblity.mm | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm b/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm index 86a98de956..641430f613 100644 --- a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm +++ b/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm @@ -41,11 +41,22 @@ static void SortAccessibilityElements(NSMutableArray *elements) [elements sortUsingComparator:comparator]; } -@implementation UIAccessibilityElement (_ASDisplayView) +@interface ASAccessibilityElement : UIAccessibilityElement -+ (UIAccessibilityElement *)accessibilityElementWithContainer:(id)container node:(ASDisplayNode *)node +@property (nonatomic, strong) ASDisplayNode *node; +@property (nonatomic, strong) ASDisplayNode *containerNode; + ++ (ASAccessibilityElement *)accessibilityElementWithContainer:(UIView *)container node:(ASDisplayNode *)node containerNode:(ASDisplayNode *)containerNode; + +@end + +@implementation ASAccessibilityElement + ++ (ASAccessibilityElement *)accessibilityElementWithContainer:(UIView *)container node:(ASDisplayNode *)node containerNode:(ASDisplayNode *)containerNode { - UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:container]; + ASAccessibilityElement *accessibilityElement = [[ASAccessibilityElement alloc] initWithAccessibilityContainer:container]; + accessibilityElement.node = node; + accessibilityElement.containerNode = containerNode; accessibilityElement.accessibilityIdentifier = node.accessibilityIdentifier; accessibilityElement.accessibilityLabel = node.accessibilityLabel; accessibilityElement.accessibilityHint = node.accessibilityHint; @@ -54,8 +65,14 @@ static void SortAccessibilityElements(NSMutableArray *elements) return accessibilityElement; } -@end +- (CGRect)accessibilityFrame +{ + CGRect accessibilityFrame = [self.containerNode convertRect:self.node.bounds fromNode:self.node]; + accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(accessibilityFrame, self.accessibilityContainer); + return accessibilityFrame; +} +@end #pragma mark - _ASDisplayView / UIAccessibilityContainer @@ -68,11 +85,7 @@ static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplay // 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 != containerNode && 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 = [containerNode convertRect:currentNode.bounds fromNode:currentNode]; - accessibilityElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(frame, container); + UIAccessibilityElement *accessibilityElement = [ASAccessibilityElement accessibilityElementWithContainer:container node:currentNode containerNode:containerNode]; [elements addObject:accessibilityElement]; } }); @@ -97,10 +110,7 @@ static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableA // An accessiblityElement can either be a UIView or a UIAccessibilityElement if (subnode.isLayerBacked) { // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node - UIAccessibilityElement *accessiblityElement = [UIAccessibilityElement accessibilityElementWithContainer:view node:subnode]; - - CGRect frame = [node convertRect:subnode.bounds fromNode:subnode]; - [accessiblityElement setAccessibilityFrame:UIAccessibilityConvertFrameToScreenCoordinates(frame, view)]; + UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:subnode containerNode:node]; [elements addObject:accessiblityElement]; } else { // Accessiblity element is not layer backed just add the view as accessibility element @@ -118,7 +128,6 @@ static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableA @interface _ASDisplayView () { NSArray *_accessibleElements; - CGRect _lastAccessibleElementsFrame; } @end @@ -139,13 +148,10 @@ static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableA return @[]; } - CGRect screenFrame = UIAccessibilityConvertFrameToScreenCoordinates(self.bounds, self); - if (_accessibleElements != nil && CGRectEqualToRect(_lastAccessibleElementsFrame, screenFrame)) { + if (_accessibleElements != nil) { return _accessibleElements; } - _lastAccessibleElementsFrame = screenFrame; - NSMutableArray *accessibleElements = [NSMutableArray array]; CollectAccessibilityElementsForView(self, accessibleElements); SortAccessibilityElements(accessibleElements);