From e6964d1ade0d16d1d247d675f768647e2d2034fa Mon Sep 17 00:00:00 2001 From: Jia Wern Lim <42153084+jiawernlim@users.noreply.github.com> Date: Fri, 19 Oct 2018 07:39:21 -0700 Subject: [PATCH] [ASDisplayNode] Expose default Texture-set accessibility values as properties (#1170) * Expose default Texture-set accessibility values as properties in ASDisplayNode. Added relevant overrides and tests. * Quick style fixes & add default a11y overrides to ASTextNode too. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 8 +++ CHANGELOG.md | 1 + Source/ASButtonNode.mm | 22 ++++-- Source/ASDisplayNode+Beta.h | 11 +++ Source/ASDisplayNode.mm | 25 +++++++ Source/ASTextNode.mm | 15 +++- Source/ASTextNode2.mm | 15 +++- Tests/ASButtonNodeTests.m | 55 +++++++++++++++ Tests/ASTextNode2Tests.m | 85 +++++++++++++++++++++++ 9 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 Tests/ASButtonNodeTests.m create mode 100644 Tests/ASTextNode2Tests.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 5d2280c29e..27d49fc5a8 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -494,6 +494,8 @@ E5E281761E71C845006B67C2 /* ASCollectionLayoutState.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5E281751E71C845006B67C2 /* ASCollectionLayoutState.mm */; }; E5E2D72E1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E5E2D72D1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; E5E2D7301EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5E2D72F1EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.mm */; }; + F325E48C21745F9E00AC93A4 /* ASButtonNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F325E48B21745F9E00AC93A4 /* ASButtonNodeTests.m */; }; + F325E490217460B100AC93A4 /* ASTextNode2Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = F325E48F217460B000AC93A4 /* ASTextNode2Tests.m */; }; F3F698D2211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F3F698D1211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm */; }; F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F711994D1D20C21100568860 /* ASDisplayNodeExtrasTests.m */; }; FA4FAF15200A850200E735BD /* ASControlNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4FAF14200A850200E735BD /* ASControlNode+Private.h */; }; @@ -1023,6 +1025,8 @@ E5E2D72D1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionGalleryLayoutDelegate.h; sourceTree = ""; }; E5E2D72F1EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionGalleryLayoutDelegate.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F325E48B21745F9E00AC93A4 /* ASButtonNodeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASButtonNodeTests.m; sourceTree = ""; }; + F325E48F217460B000AC93A4 /* ASTextNode2Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTextNode2Tests.m; sourceTree = ""; }; F3F698D1211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayViewAccessibilityTests.mm; sourceTree = ""; }; F711994D1D20C21100568860 /* ASDisplayNodeExtrasTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeExtrasTests.m; sourceTree = ""; }; FA4FAF14200A850200E735BD /* ASControlNode+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Private.h"; sourceTree = ""; }; @@ -1251,6 +1255,8 @@ 058D09C5195D04C000B7D73C /* Tests */ = { isa = PBXGroup; children = ( + F325E48F217460B000AC93A4 /* ASTextNode2Tests.m */, + F325E48B21745F9E00AC93A4 /* ASButtonNodeTests.m */, F3F698D1211CAD4600800CB1 /* ASDisplayViewAccessibilityTests.mm */, DBC452DD1C5C6A6A00B16017 /* ArrayDiffingTests.m */, AC026B571BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.m */, @@ -2272,6 +2278,7 @@ CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */, 052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */, 058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */, + F325E48C21745F9E00AC93A4 /* ASButtonNodeTests.m in Sources */, E586F96C1F9F9E2900ECE00E /* ASScrollNodeTests.m in Sources */, CC8B05D81D73979700F54286 /* ASTextNodePerformanceTests.m in Sources */, CC583AD91EF9BDC600134156 /* ASDisplayNode+OCMock.m in Sources */, @@ -2296,6 +2303,7 @@ 3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */, AEEC47E41C21D3D200EC1693 /* ASVideoNodeTests.m in Sources */, 254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */, + F325E490217460B100AC93A4 /* ASTextNode2Tests.m in Sources */, 058D0A3D195D057000B7D73C /* ASTextKitCoreTextAdditionsTests.m in Sources */, CC3B20901C3F892D00798563 /* ASBridgedPropertiesTests.mm in Sources */, CCE4F9BE1F0ECE5200062E4E /* ASTLayoutFixture.mm in Sources */, diff --git a/CHANGELOG.md b/CHANGELOG.md index e1898e1eb5..5b94c817ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ - Fix crash setting attributed text on multiple threads [Michael Schneider](https://github.com/maicki) - [ASTextNode2] Ignore certain text alignments while calculating intrinsic size [Huy Nguyen](https://github.com/nguyenhuy) [#1166](https://github.com/TextureGroup/Texture/pull/1166) - Fix mismatch in UIAccessibilityAction selector method [Michael Schneider](https://github.com/maicki) [#1169](https://github.com/TextureGroup/Texture/pull/1169) +- [ASDisplayNode] Expose default Texture-set accessibility values as properties. [Jia Wern Lim](https://github.com/jiawernlim) [#1170](https://github.com/TextureGroup/Texture/pull/1170) ## 2.7 - Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877) diff --git a/Source/ASButtonNode.mm b/Source/ASButtonNode.mm index f0ca75eed2..e4bbb88633 100644 --- a/Source/ASButtonNode.mm +++ b/Source/ASButtonNode.mm @@ -63,7 +63,7 @@ _contentVerticalAlignment = ASVerticalAlignmentCenter; _contentEdgeInsets = UIEdgeInsetsZero; _imageAlignment = ASButtonNodeImageAlignmentBeginning; - self.accessibilityTraits = UIAccessibilityTraitButton; + self.accessibilityTraits = self.defaultAccessibilityTraits;; } return self; } @@ -114,11 +114,7 @@ { if (self.enabled != enabled) { [super setEnabled:enabled]; - if (enabled) { - self.accessibilityTraits = UIAccessibilityTraitButton; - } else { - self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled; - } + self.accessibilityTraits = self.defaultAccessibilityTraits; [self updateButtonContent]; } } @@ -204,7 +200,7 @@ _titleNode.attributedText = newTitle; [self unlock]; - self.accessibilityLabel = _titleNode.accessibilityLabel; + self.accessibilityLabel = self.defaultAccessibilityLabel; [self setNeedsLayout]; return; } @@ -544,6 +540,18 @@ return spec; } +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _titleNode.accessibilityLabel; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return self.enabled ? UIAccessibilityTraitButton + : (UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled); +} + - (void)layout { [super layout]; diff --git a/Source/ASDisplayNode+Beta.h b/Source/ASDisplayNode+Beta.h index 5907ac9b1c..affe99cb25 100644 --- a/Source/ASDisplayNode+Beta.h +++ b/Source/ASDisplayNode+Beta.h @@ -109,6 +109,17 @@ typedef struct { */ @property BOOL isAccessibilityContainer; +/** + * @abstract Returns the default accessibility property values set by Texture on this node. For + * example, the default accessibility label for a text node may be its text content, while most + * other nodes would have nil default labels. + */ +@property (nullable, readonly, copy) NSString *defaultAccessibilityLabel; +@property (nullable, readonly, copy) NSString *defaultAccessibilityHint; +@property (nullable, readonly, copy) NSString *defaultAccessibilityValue; +@property (nullable, readonly, copy) NSString *defaultAccessibilityIdentifier; +@property (readonly) UIAccessibilityTraits defaultAccessibilityTraits; + /** * @abstract Invoked when a user performs a custom action on an accessible node. Nodes that are children of accessibility containers, have * an accessibity label and have an interactive UIAccessibilityTrait will automatically receive custom-action handling. diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 52e389b2e3..7dd5c7d79c 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -3632,6 +3632,31 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { return _isAccessibilityContainer; } +- (NSString *)defaultAccessibilityLabel +{ + return nil; +} + +- (NSString *)defaultAccessibilityHint +{ + return nil; +} + +- (NSString *)defaultAccessibilityValue +{ + return nil; +} + +- (NSString *)defaultAccessibilityIdentifier +{ + return nil; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitNone; +} + #pragma mark - Debugging (Private) #if ASEVENTLOG_ENABLE diff --git a/Source/ASTextNode.mm b/Source/ASTextNode.mm index 7f0cc3f1a3..c29d03312c 100644 --- a/Source/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -226,7 +226,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Accessibility self.isAccessibilityElement = YES; - self.accessibilityTraits = UIAccessibilityTraitStaticText; + self.accessibilityTraits = self.defaultAccessibilityTraits; // Placeholders // Disabled by default in ASDisplayNode, but add a few options for those who toggle @@ -365,6 +365,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; }; } +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _attributedText.string; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitStaticText; +} + #pragma mark - Layout and Sizing - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset @@ -474,7 +485,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Accessiblity let currentAttributedText = self.attributedText; // Grab attributed string again in case it changed in the meantime - self.accessibilityLabel = currentAttributedText.string; + self.accessibilityLabel = self.defaultAccessibilityLabel; self.isAccessibilityElement = (currentAttributedText.length != 0); // We're an accessibility element by default if there is a string. #if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS diff --git a/Source/ASTextNode2.mm b/Source/ASTextNode2.mm index f3d5487b82..cca660f136 100644 --- a/Source/ASTextNode2.mm +++ b/Source/ASTextNode2.mm @@ -115,7 +115,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // Accessibility self.isAccessibilityElement = YES; - self.accessibilityTraits = UIAccessibilityTraitStaticText; + self.accessibilityTraits = self.defaultAccessibilityTraits; // Placeholders // Disabled by default in ASDisplayNode, but add a few options for those who toggle @@ -209,6 +209,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; return YES; } +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _attributedText.string; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitStaticText; +} + #pragma mark - Layout and Sizing - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset @@ -304,7 +315,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; [self setNeedsDisplay]; // Accessiblity - self.accessibilityLabel = attributedText.string; + self.accessibilityLabel = self.defaultAccessibilityLabel; self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string. #if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS diff --git a/Tests/ASButtonNodeTests.m b/Tests/ASButtonNodeTests.m new file mode 100644 index 0000000000..1c0c0adb61 --- /dev/null +++ b/Tests/ASButtonNodeTests.m @@ -0,0 +1,55 @@ +// +// ASButtonNodeTests.m +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +@interface ASButtonNodeTests : XCTestCase +@end + +@implementation ASButtonNodeTests + +- (void)testAccessibility +{ + // Setup a button with some title. + ASButtonNode *buttonNode = nil; + buttonNode = [[ASButtonNode alloc] init]; + NSString *title = @"foo"; + [buttonNode setTitle:title withFont:nil withColor:nil forState:UIControlStateNormal]; + + // Verify accessibility properties. + XCTAssertTrue(buttonNode.accessibilityTraits == UIAccessibilityTraitButton, + @"Should have button accessibility trait, instead has %llu", + buttonNode.accessibilityTraits); + XCTAssertTrue(buttonNode.defaultAccessibilityTraits == UIAccessibilityTraitButton, + @"Default accessibility traits should return button accessibility trait, instead " + @"returns %llu", + buttonNode.defaultAccessibilityTraits); + XCTAssertTrue([buttonNode.accessibilityLabel isEqualToString:title], + @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", + buttonNode.accessibilityLabel, title); + XCTAssertTrue([buttonNode.defaultAccessibilityLabel isEqualToString:title], + @"Default accessibility label incorrectly returns \n%@\n when it should be \n%@\n", + buttonNode.defaultAccessibilityLabel, title); + + // Disable the button and verify that accessibility traits has been updated correctly. + buttonNode.enabled = NO; + UIAccessibilityTraits disabledButtonTrait = + UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled; + XCTAssertTrue(buttonNode.accessibilityTraits == disabledButtonTrait, + @"Should have disabled button accessibility trait, instead has %llu", + buttonNode.accessibilityTraits); + XCTAssertTrue(buttonNode.defaultAccessibilityTraits == disabledButtonTrait, + @"Default accessibility traits should return disabled button accessibility trait, " + @"instead returns %llu", + buttonNode.defaultAccessibilityTraits); +} + +@end diff --git a/Tests/ASTextNode2Tests.m b/Tests/ASTextNode2Tests.m new file mode 100644 index 0000000000..74e9399dd2 --- /dev/null +++ b/Tests/ASTextNode2Tests.m @@ -0,0 +1,85 @@ +// +// ASTextNode2Tests.m +// TextureTests +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import "ASTestCase.h" + +#import +#import + +#import + +@interface ASTextNode2Tests : XCTestCase + +@property(nonatomic) ASTextNode2 *textNode; +@property(nonatomic, copy) NSAttributedString *attributedText; + +@end + +@implementation ASTextNode2Tests + +- (void)setUp +{ + [super setUp]; + _textNode = [[ASTextNode2 alloc] init]; + + UIFontDescriptor *desc = [UIFontDescriptor fontDescriptorWithName:@"Didot" size:18]; + NSArray *arr = @[ @{ + UIFontFeatureTypeIdentifierKey : @(kLetterCaseType), + UIFontFeatureSelectorIdentifierKey : @(kSmallCapsSelector) + } ]; + desc = [desc fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : arr}]; + UIFont *f = [UIFont fontWithDescriptor:desc size:0]; + NSDictionary *d = @{NSFontAttributeName : f}; + NSMutableAttributedString *mas = [[NSMutableAttributedString alloc] + initWithString: + @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor " + @"incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " + @"exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + @"dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + @"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + @"mollit anim id est laborum." + attributes:d]; + NSMutableParagraphStyle *para = [NSMutableParagraphStyle new]; + para.alignment = NSTextAlignmentCenter; + para.lineSpacing = 1.0; + [mas addAttribute:NSParagraphStyleAttributeName value:para range:NSMakeRange(0, mas.length - 1)]; + + // Vary the linespacing on the last line + NSMutableParagraphStyle *lastLinePara = [NSMutableParagraphStyle new]; + lastLinePara.alignment = para.alignment; + lastLinePara.lineSpacing = 5.0; + [mas addAttribute:NSParagraphStyleAttributeName + value:lastLinePara + range:NSMakeRange(mas.length - 1, 1)]; + + _attributedText = mas; + _textNode.attributedText = _attributedText; +} + +- (void)testAccessibility +{ + XCTAssertTrue(_textNode.isAccessibilityElement, @"Should be an accessibility element"); + XCTAssertTrue(_textNode.accessibilityTraits == UIAccessibilityTraitStaticText, + @"Should have static text accessibility trait, instead has %llu", + _textNode.accessibilityTraits); + XCTAssertTrue(_textNode.defaultAccessibilityTraits == UIAccessibilityTraitStaticText, + @"Default accessibility traits should return static text accessibility trait, " + @"instead returns %llu", + _textNode.defaultAccessibilityTraits); + + XCTAssertTrue([_textNode.accessibilityLabel isEqualToString:_attributedText.string], + @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", + _textNode.accessibilityLabel, _attributedText.string); + XCTAssertTrue([_textNode.defaultAccessibilityLabel isEqualToString:_attributedText.string], + @"Default accessibility label incorrectly returns \n%@\n when it should be \n%@\n", + _textNode.defaultAccessibilityLabel, _attributedText.string); +} + +@end