Skip to content

Commit

Permalink
Support modifying/overriding value returned by accessibilityElements
Browse files Browse the repository at this point in the history
Sometimes we want to override `accessibilityElements` so that we can
skip over elements unnecessary for interacting with the app using VoiceOver
or limit the interaction to child viewcontrollers presented, etc.

For this purpose, this commit introduces `accessibilityElementsBlock`
that receives the default `accessibilityElements` as input and modifies the
elements returned. If not set, `accessibilityElements` should still return
the elements found from the node hierarchy.
  • Loading branch information
raycsh017 committed May 22, 2019
1 parent 64c43c0 commit 187108b
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,12 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority;

@end

/**
* Accessibility elements modification block. Used to modify/override the elements returned by
* `accessibilityElements` for a node.
*/
typedef NSArray *_Nonnull(^ASDisplayNodeAccessibilityElementsBlock)(NSArray * _Nonnull);

@interface ASDisplayNode (UIViewBridgeAccessibility)

// Accessibility support
Expand All @@ -805,13 +811,17 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority;
@property BOOL shouldGroupAccessibilityChildren;
@property UIAccessibilityNavigationStyle accessibilityNavigationStyle;
@property (nullable, copy) NSArray *accessibilityCustomActions API_AVAILABLE(ios(8.0),tvos(9.0));

#if TARGET_OS_TV
@property (nullable, copy) NSArray *accessibilityHeaderElements;
#endif

// Accessibility identification support
@property (nullable, copy) NSString *accessibilityIdentifier;

/// Block used for modifying/overriding the a11y elements returned by `accessibilityElements`.
@property (nullable, copy) ASDisplayNodeAccessibilityElementsBlock accessibilityElementsBlock;

@end

@interface ASDisplayNode (ASLayoutElement) <ASLayoutElement>
Expand Down
12 changes: 12 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3546,6 +3546,18 @@ - (BOOL)isAccessibilityContainer
return _isAccessibilityContainer;
}

- (void)setAccessibilityElementsBlock:(ASDisplayNodeAccessibilityElementsBlock)accessibilityElementsBlock
{
MutexLocker l(__instanceLock__);
_accessibilityElementsBlock = accessibilityElementsBlock;
}

- (ASDisplayNodeAccessibilityElementsBlock)accessibilityElementsBlock
{
MutexLocker l(__instanceLock__);
return _accessibilityElementsBlock;
}

- (NSString *)defaultAccessibilityLabel
{
return nil;
Expand Down
6 changes: 6 additions & 0 deletions Source/Details/_ASDisplayViewAccessiblity.mm
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ - (NSArray *)accessibilityElements
NSMutableArray *accessibilityElements = [[NSMutableArray alloc] init];
CollectAccessibilityElementsForView(self.view, accessibilityElements);
SortAccessibilityElements(accessibilityElements);

if (_accessibilityElementsBlock) {
NSArray *accessibilityElementsCopy = [accessibilityElements copy];
accessibilityElements = _accessibilityElementsBlock(accessibilityElementsCopy);
}

return accessibilityElements;
}

Expand Down
1 change: 1 addition & 0 deletions Source/Private/ASDisplayNodeInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ static constexpr CACornerMask kASCACornerAllCorners =
CGPoint _accessibilityActivationPoint;
UIBezierPath *_accessibilityPath;
BOOL _isAccessibilityContainer;
ASDisplayNodeAccessibilityElementsBlock _accessibilityElementsBlock;


// Safe Area support
Expand Down
34 changes: 34 additions & 0 deletions Tests/ASDisplayViewAccessibilityTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,40 @@ - (void)testAccessibilityElementsAccessors
XCTAssertEqual([node.view indexOfAccessibilityElement:node.view.accessibilityElements.firstObject], 0);*/
}

- (void)testAccessibilityElementsBlock
{
// Setup nodes
ASDisplayNode *node = nil;
ASDisplayNode *innerNode1 = nil;
ASDisplayNode *innerNode2 = nil;
node = [[ASDisplayNode alloc] init];
innerNode1 = [[ASDisplayNode alloc] init];
innerNode2 = [[ASDisplayNode alloc] init];

innerNode1.accessibilityLabel = @"hello";
innerNode1.isAccessibilityElement = YES;
innerNode2.accessibilityLabel = @"world";
innerNode2.isAccessibilityElement = YES;

// Attach the subnodes to the parent node, then check that all the subnodes added are returned as accessibilityElements
[node addSubnode:innerNode1];
[node addSubnode:innerNode2];

XCTAssertTrue(node.view.accessibilityElements.count == 2);
XCTAssertEqualObjects([node.view.accessibilityElements[0] accessibilityLabel], [innerNode1 accessibilityLabel]);
XCTAssertEqualObjects([node.view.accessibilityElements[1] accessibilityLabel], [innerNode2 accessibilityLabel]);

// Override accessibilityElements, check that only the specified element is returned as accessibilityElements
node.accessibilityElementsBlock = ^NSArray * _Nonnull(NSArray *_Nonnull elements) {
return @[innerNode2];
};

XCTAssertTrue(node.view.accessibilityElements.count == 1);
XCTAssertEqualObjects([node.view.accessibilityElements.firstObject accessibilityLabel],
[innerNode2 accessibilityLabel],
@"Parent node accessibilityElements override broken");
}

- (void)testThatSubnodeAccessibilityLabelAggregationWorks
{
// Setup nodes
Expand Down

0 comments on commit 187108b

Please sign in to comment.