Skip to content

Commit e16fdd6

Browse files
authored
[ASDisplayNode] Implement accessibilityElementsHidden (#1859)
Most of this code comes from an old PR that @fruitcoder put up #795 2 years ago. When creating our array of accessibilityElements, we need to respect the value of `accessibilityElementsHidden`. If the value of this property changes, we need to invalidate the cached accessibility elements (unless we are in the experiment that doesn’t cache `accessibilityElements`). I created a simple test app and made sure this matched UIKit’s implementation. I also added a test case that changes the value of `accessibilityElementsHidden` and makes sure the proper accessibilityElements are returned.
1 parent 8b9683d commit e16fdd6

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

Source/Details/_ASDisplayViewAccessiblity.mm

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ + (ASAccessibilityElement *)accessibilityElementWithContainer:(UIView *)containe
8989
accessibilityElement.accessibilityHint = node.accessibilityHint;
9090
accessibilityElement.accessibilityValue = node.accessibilityValue;
9191
accessibilityElement.accessibilityTraits = node.accessibilityTraits;
92+
accessibilityElement.accessibilityElementsHidden = node.accessibilityElementsHidden;
9293
if (AS_AVAILABLE_IOS_TVOS(11, 11)) {
9394
accessibilityElement.accessibilityAttributedLabel = node.accessibilityAttributedLabel;
9495
accessibilityElement.accessibilityAttributedHint = node.accessibilityAttributedHint;
@@ -221,6 +222,11 @@ static BOOL recusivelyCheckSuperviewsForScrollView(UIView *view) {
221222
return recusivelyCheckSuperviewsForScrollView(view.superview);
222223
}
223224

225+
/// returns YES if this node should be considered "hidden" from the screen reader.
226+
static BOOL nodeIsHiddenFromAcessibility(ASDisplayNode *node) {
227+
return node.isHidden || node.alpha == 0.0 || node.accessibilityElementsHidden;
228+
}
229+
224230
/// Collect all accessibliity elements for a given view and view node
225231
static void CollectAccessibilityElements(ASDisplayNode *node, NSMutableArray *elements)
226232
{
@@ -254,6 +260,10 @@ static void CollectAccessibilityElements(ASDisplayNode *node, NSMutableArray *el
254260
return;
255261
}
256262

263+
if (nodeIsHiddenFromAcessibility(node)) {
264+
return;
265+
}
266+
257267
// see if one of the subnodes is modal. If it is, then we only need to collect accessibilityElements from that
258268
// node. If more than one subnode is modal, UIKit uses the last view in subviews as the modal view (it appears to
259269
// be based on the index in the subviews array, not the location on screen). Let's do the same.
@@ -270,7 +280,7 @@ static void CollectAccessibilityElements(ASDisplayNode *node, NSMutableArray *el
270280

271281
for (ASDisplayNode *subnode in subnodes) {
272282
// If a node is hidden or has an alpha of 0.0 we should not include it
273-
if (subnode.hidden || subnode.alpha == 0.0) {
283+
if (nodeIsHiddenFromAcessibility(subnode)) {
274284
continue;
275285
}
276286

Source/Private/ASDisplayNode+UIViewBridge.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,13 @@ - (BOOL)accessibilityElementsHidden
13101310
- (void)setAccessibilityElementsHidden:(BOOL)accessibilityElementsHidden
13111311
{
13121312
_bridge_prologue_write;
1313+
BOOL oldHiddenValue = _getFromViewOnly(accessibilityElementsHidden);
13131314
_setAccessibilityToViewAndProperty(_flags.accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden);
1315+
1316+
// if we made a change, we need to clear the view's accessibilityElements cache.
1317+
if (!ASActivateExperimentalFeature(ASExperimentalDoNotCacheAccessibilityElements) && self.isNodeLoaded && oldHiddenValue != accessibilityElementsHidden) {
1318+
[self invalidateAccessibilityElements];
1319+
}
13141320
}
13151321

13161322
- (BOOL)accessibilityViewIsModal

Tests/ASDisplayViewAccessibilityTests.mm

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,5 +591,42 @@ - (void)testMultipleSubnodesAreModal {
591591
XCTAssertTrue([elements containsObject:modalNode2.view]);
592592
}
593593

594+
- (void)testAccessibilityElementsHidden {
595+
596+
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 320, 568)];
597+
ASDisplayNode *node = [[ASDisplayNode alloc] init];
598+
node.automaticallyManagesSubnodes = YES;
599+
600+
ASViewController *vc = [[ASViewController alloc] initWithNode:node];
601+
window.rootViewController = vc;
602+
[window makeKeyAndVisible];
603+
[window layoutIfNeeded];
604+
605+
ASTextNode *label1 = [[ASTextNode alloc] init];
606+
label1.attributedText = [[NSAttributedString alloc] initWithString:@"on screen"];
607+
label1.frame = CGRectMake(0, 0, 100, 20);
608+
609+
ASTextNode *label2 = [[ASTextNode alloc] init];
610+
label2.attributedText = [[NSAttributedString alloc] initWithString:@"partially on screen y"];
611+
label2.frame = CGRectMake(0, 20, 100, 20);
612+
613+
[node addSubnode:label1];
614+
[node addSubnode:label2];
615+
616+
NSArray *elements = [node.view accessibilityElements];
617+
XCTAssertTrue(elements.count == 2);
618+
XCTAssertTrue([elements containsObject:label1.view]);
619+
XCTAssertTrue([elements containsObject:label2.view]);
620+
621+
node.accessibilityElementsHidden = YES;
622+
elements = [node.view accessibilityElements];
623+
XCTAssertTrue(elements.count == 0);
624+
625+
node.accessibilityElementsHidden = NO;
626+
elements = [node.view accessibilityElements];
627+
XCTAssertTrue(elements.count == 2);
628+
XCTAssertTrue([elements containsObject:label1.view]);
629+
XCTAssertTrue([elements containsObject:label2.view]);
630+
}
594631

595632
@end

0 commit comments

Comments
 (0)