Skip to content
This repository has been archived by the owner on May 3, 2023. It is now read-only.

Commit

Permalink
[ASDisplayNode] Allow explicit setting of accessibilityElements (Text…
Browse files Browse the repository at this point in the history
…ureGroup#1807)

* [ASDisplayNode] Allow explicit setting of accessibilityElements

Since `NSObject` conforms to the informal accessibility protocol, `ASDisplayNode` has an inherited property for `accessibilityElements`. However, setting this property has no effect since `ASDisplayNode` overrides the `accessibilityElements` getter. Added a small change to the getter to check if the `accessibilityElements` property on `ASDisplayNode` has been explicitly set, and if so return that.

I also added a comment around  `_ASDisplayView`’s `setAccessibilityElements:` method to clear up some (of my own) confusion.

* comment tweak

* Fix flakey test
  • Loading branch information
rcancro authored and piotrdebosz committed Mar 1, 2021
1 parent 881f038 commit 476fe3c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
12 changes: 12 additions & 0 deletions Source/Details/_ASDisplayViewAccessiblity.mm
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ @implementation _ASDisplayView (UIAccessibilityContainer)
- (void)setAccessibilityElements:(NSArray *)accessibilityElements
{
ASDisplayNodeAssertMainThread();
// While it looks very strange to ignore the accessibilyElements param and set _accessibilityElements to nil, it is actually on purpose.
// _ASDisplayView's accessibilityElements method will always defer to the node for accessibilityElements when _accessibilityElements is
// nil. Calling setAccessibilityElements on _ASDisplayView is basically clearing the cache and forcing _ASDisplayView to ask the node
// for its accessibilityElements the next time they are requested.
_accessibilityElements = nil;
}

Expand All @@ -283,6 +287,14 @@ @implementation ASDisplayNode (AccessibilityInternal)

- (NSArray *)accessibilityElements
{
// NSObject implements the informal accessibility protocol. This means that all ASDisplayNodes already have an accessibilityElements
// property. If an ASDisplayNode subclass has explicitly set the property, let's use that instead of traversing the node tree to try
// to create the elements automatically
NSArray *elements = [super accessibilityElements];
if (elements.count) {
return elements;
}

if (!self.isNodeLoaded) {
ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since node is not loaded");
return @[];
Expand Down
58 changes: 58 additions & 0 deletions Tests/ASDisplayViewAccessibilityTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#import <XCTest/XCTest.h>

#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/ASButtonNode.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASTextNode.h>
Expand Down Expand Up @@ -193,4 +195,60 @@ - (void)testActionForwarding {
OCMVerifyAll(mockNode);
}

#pragma mark -
#pragma mark AccessibilityElements

// dummy action for a button
- (void)fakeSelector:(id)sender { }

- (void)testThatAccessibilityElementsWorks {
ASDisplayNode *containerNode = [[ASDisplayNode alloc] init];
containerNode.frame = CGRectMake(0, 0, 100, 200);

ASTextNode *label = [[ASTextNode alloc] init];
label.attributedText = [[NSAttributedString alloc] initWithString:@"test label"];
label.frame = CGRectMake(0, 0, 100, 20);

ASButtonNode *button = [[ASButtonNode alloc] init];
[button setTitle:@"tap me" withFont:[UIFont systemFontOfSize:17] withColor:nil forState:UIControlStateNormal];
[button addTarget:self action:@selector(fakeSelector:) forControlEvents:ASControlNodeEventTouchUpInside];
button.frame = CGRectMake(0, 25, 100, 20);

[containerNode addSubnode:label];
[containerNode addSubnode:button];

// force load
__unused UIView *view = containerNode.view;

NSArray *elements = [containerNode accessibilityElements];
XCTAssertTrue(elements.count == 2);
XCTAssertEqual([elements.firstObject asyncdisplaykit_node], label);
XCTAssertEqual([elements.lastObject asyncdisplaykit_node], button);
}

- (void)testThatAccessibilityElementsOverrideWorks {
ASDisplayNode *containerNode = [[ASDisplayNode alloc] init];
containerNode.frame = CGRectMake(0, 0, 100, 200);

ASTextNode *label = [[ASTextNode alloc] init];
label.attributedText = [[NSAttributedString alloc] initWithString:@"test label"];
label.frame = CGRectMake(0, 0, 100, 20);

ASButtonNode *button = [[ASButtonNode alloc] init];
[button setTitle:@"tap me" withFont:[UIFont systemFontOfSize:17] withColor:nil forState:UIControlStateNormal];
[button addTarget:self action:@selector(fakeSelector:) forControlEvents:ASControlNodeEventTouchUpInside];
button.frame = CGRectMake(0, 25, 100, 20);

[containerNode addSubnode:label];
[containerNode addSubnode:button];
containerNode.accessibilityElements = @[ label ];

// force load
__unused UIView *view = containerNode.view;

NSArray *elements = [containerNode accessibilityElements];
XCTAssertTrue(elements.count == 1);
XCTAssertEqual(elements.firstObject, label);
}

@end
3 changes: 2 additions & 1 deletion Tests/ASViewControllerTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ - (void)testThatViewControllerFrameIsRightAfterCustomTransitionWithNonextendedEd
nav.delegate = navDelegate;
window.rootViewController = nav;
[window makeKeyAndVisible];
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[nav pushViewController:vc animated:YES];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];

[self waitForExpectationsWithTimeout:2 handler:nil];

Expand Down

0 comments on commit 476fe3c

Please sign in to comment.