From 9261035c2bf2fe9522806fb1c535a1835e7acfa2 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 8 May 2019 03:44:40 -0700 Subject: [PATCH] Move accessibility props to UIView+React (#24743) Summary: React Native Gesture Handler uses a `RCTViewManager` subclass to manage `UIControl` so the cast to set accessibility props is not safe. This moves the accessibility props we set to `UIView+React` so they can be used safely on any `UIView`. ![image](https://user-images.githubusercontent.com/2677334/57042641-46e42700-6c33-11e9-9a97-76661ad5d14d.png) [General] [Fixed] - Move accessibility props to UIView+React Pull Request resolved: https://github.com/facebook/react-native/pull/24743 Differential Revision: D15258062 Pulled By: cpojer fbshipit-source-id: 4a25b79407e5cb7b3b368c7506161e989794bb26 --- React/Views/RCTView.h | 7 ------- React/Views/RCTView.m | 10 +++++----- React/Views/RCTViewManager.m | 6 +++--- React/Views/UIView+React.h | 7 +++++++ React/Views/UIView+React.m | 30 ++++++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 2467ca1333f9af..a63ef765bee82d 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -28,13 +28,6 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait; @property (nonatomic, copy) RCTDirectEventBlock onMagicTap; @property (nonatomic, copy) RCTDirectEventBlock onAccessibilityEscape; -/** - * Accessibility properties - */ -@property (nonatomic, copy) NSArray *accessibilityActions; -@property (nonatomic, copy) NSString *accessibilityRole; -@property (nonatomic, copy) NSArray *accessibilityStates; - /** * Used to control how touch events are processed. */ diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index ea305016e453ba..04795ef96ad394 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -158,12 +158,12 @@ - (NSString *)accessibilityLabel - (NSArray *)accessibilityCustomActions { - if (!_accessibilityActions.count) { + if (!self.accessibilityActions.count) { return nil; } NSMutableArray *actions = [NSMutableArray array]; - for (NSString *action in _accessibilityActions) { + for (NSString *action in self.accessibilityActions) { [actions addObject:[[UIAccessibilityCustomAction alloc] initWithName:action target:self selector:@selector(didActivateAccessibilityCustomAction:)]]; @@ -189,7 +189,7 @@ - (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)acti - (NSString *)accessibilityValue { if ((self.accessibilityTraits & SwitchAccessibilityTrait) == SwitchAccessibilityTrait) { - for (NSString *state in _accessibilityStates) { + for (NSString *state in self.accessibilityStates) { if ([state isEqualToString:@"checked"]) { return @"1"; } else if ([state isEqualToString:@"unchecked"]) { @@ -231,11 +231,11 @@ - (NSString *)accessibilityValue @"collapsed" : @"collapsed", }; }); - NSString *roleDescription = _accessibilityRole ? roleDescriptions[_accessibilityRole]: nil; + NSString *roleDescription = self.accessibilityRole ? roleDescriptions[self.accessibilityRole]: nil; if (roleDescription) { [valueComponents addObject:roleDescription]; } - for (NSString *state in _accessibilityStates) { + for (NSString *state in self.accessibilityStates) { NSString *stateDescription = state ? stateDescriptions[state] : nil; if (stateDescription) { [valueComponents addObject:stateDescription]; diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 6efc77c07771a8..737686f799627d 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -174,7 +174,7 @@ - (RCTShadowView *)shadowView view.reactAccessibilityElement.accessibilityTraits |= maskedTraits; } else { NSString *role = json ? [RCTConvert NSString:json] : @""; - ((RCTView *)view.reactAccessibilityElement).accessibilityRole = role; + view.reactAccessibilityElement.accessibilityRole = role; } } @@ -200,9 +200,9 @@ - (RCTShadowView *)shadowView } } if (newStates.count > 0) { - ((RCTView *)view.reactAccessibilityElement).accessibilityStates = newStates; + view.reactAccessibilityElement.accessibilityStates = newStates; } else { - ((RCTView *)view.reactAccessibilityElement).accessibilityStates = nil; + view.reactAccessibilityElement.accessibilityStates = nil; } } diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index d7d7aa7d6bbad0..2f510654244fb5 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -113,6 +113,13 @@ */ @property (nonatomic, readonly) UIView *reactAccessibilityElement; +/** + * Accessibility properties + */ +@property (nonatomic, copy) NSArray *accessibilityActions; +@property (nonatomic, copy) NSString *accessibilityRole; +@property (nonatomic, copy) NSArray *accessibilityStates; + /** * Used in debugging to get a description of the view hierarchy rooted at * the current view. diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index d5098d25a7c381..019eebcc120f7f 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -297,6 +297,36 @@ - (UIView *)reactAccessibilityElement return self; } +- (NSArray *)accessibilityActions +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAccessibilityActions:(NSArray *)accessibilityActions +{ + objc_setAssociatedObject(self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSString *)accessibilityRole +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAccessibilityRole:(NSString *)accessibilityRole +{ + objc_setAssociatedObject(self, @selector(accessibilityRole), accessibilityRole, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSArray *)accessibilityStates +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAccessibilityStates:(NSArray *)accessibilityStates +{ + objc_setAssociatedObject(self, @selector(accessibilityStates), accessibilityStates, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + #pragma mark - Debug - (void)react_addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level