From 072d2709df5ca866a300f58ab4d2934aa0fdde66 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 2 Jun 2017 14:17:56 -0700 Subject: [PATCH] Introducing `-[RCTView reactAccessibleView]` Summary: Sometimes, when we implement some custom RN view, we have to proxy all accessible atributes directly to some subview which actually has accesible content. So, in other words, this allows bypass some axillary views in terms of accessibility. Concreate example which this approach supposed to fix: https://github.com/facebook/react-native/pull/14200/files#diff-e5f6b1386b7ba07fd887bca11ec828a4R208 Reviewed By: mmmulani Differential Revision: D5143860 fbshipit-source-id: 6d7ce747f28e5a31d32c925b8ad8fd4b98ce1de1 --- Libraries/Text/RCTTextField.m | 7 +++++++ Libraries/Text/RCTTextView.m | 7 +++++++ RNTester/js/TextInputExample.ios.js | 2 ++ React/Views/RCTView.m | 14 ++++++++++++++ React/Views/RCTViewManager.m | 16 +++++++++------- React/Views/UIView+React.h | 9 +++++++++ React/Views/UIView+React.m | 7 +++++++ 7 files changed, 55 insertions(+), 7 deletions(-) diff --git a/Libraries/Text/RCTTextField.m b/Libraries/Text/RCTTextField.m index b6b632a84..a6214fe81 100644 --- a/Libraries/Text/RCTTextField.m +++ b/Libraries/Text/RCTTextField.m @@ -357,6 +357,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) eventCount:_nativeEventCount]; } +#pragma mark - Accessibility + +- (UIView *)reactAccessibilityElement +{ + return _textField; +} + #pragma mark - Focus control deledation - (void)reactFocus diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index 2e63a4b2c..cea4f8726 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -530,6 +530,13 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, eventCount:_nativeEventCount]; } +#pragma mark - Accessibility + +- (UIView *)reactAccessibilityElement +{ + return _textView; +} + #pragma mark - Focus control deledation - (void)reactFocus diff --git a/RNTester/js/TextInputExample.ios.js b/RNTester/js/TextInputExample.ios.js index 88d6f3611..59fd36eb6 100644 --- a/RNTester/js/TextInputExample.ios.js +++ b/RNTester/js/TextInputExample.ios.js @@ -779,6 +779,7 @@ exports.examples = [ padding: 10, paddingTop: 20, }} + testID="singleline_textinput" placeholder="Placeholder defines intrinsic size" /> @@ -798,6 +799,7 @@ exports.examples = [ paddingTop: 20, maxHeight: 100 }} + testID="multiline_textinput" multiline={true} placeholder="Placeholder defines intrinsic size" /> diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 471edcd8a..c434a0290 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -211,6 +211,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) return CGRectContainsPoint(hitFrame, point); } +- (UIView *)reactAccessibilityElement +{ + return self; +} + +- (BOOL)isAccessibilityElement +{ + if (self.reactAccessibilityElement == self) { + return [super isAccessibilityElement]; + } + + return NO; +} + - (BOOL)accessibilityActivate { if (_onAccessibilityTap) { diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 7bbc97410..2314bef0b 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -114,12 +114,16 @@ RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL) RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary) #endif -RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement, BOOL) -RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString) -RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits) -RCT_EXPORT_VIEW_PROPERTY(accessibilityViewIsModal, BOOL) +// Acessibility related properties +RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL) +RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString) +RCT_REMAP_VIEW_PROPERTY(accessibilityTraits, reactAccessibilityElement.accessibilityTraits, UIAccessibilityTraits) +RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL) +RCT_REMAP_VIEW_PROPERTY(onAccessibilityTap, reactAccessibilityElement.onAccessibilityTap, RCTDirectEventBlock) +RCT_REMAP_VIEW_PROPERTY(onMagicTap, reactAccessibilityElement.onMagicTap, RCTDirectEventBlock) +RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString) + RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t) RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat) RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor) @@ -220,8 +224,6 @@ RCT_CUSTOM_VIEW_PROPERTY(hitSlop, UIEdgeInsets, RCTView) } } } -RCT_EXPORT_VIEW_PROPERTY(onAccessibilityTap, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onMagicTap, RCTDirectEventBlock) #define RCT_VIEW_BORDER_PROPERTY(SIDE) \ RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, float, RCTView) \ diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index c610e9aa3..ce571f12b 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -86,6 +86,15 @@ @property (nonatomic, readonly) UIEdgeInsets reactCompoundInsets; @property (nonatomic, readonly) CGRect reactContentFrame; +/** + * The (sub)view which represents this view in terms of accessibility. + * ViewManager will apply all accessibility properties directly to this view. + * May be overriten in view subclass which needs to be accessiblitywise + * transparent in favour of some subview. + * Defaults to `self`. + */ +@property (nonatomic, readonly) UIView *reactAccessibilityElement; + #if RCT_DEV /** diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index fe7d3c5f1..89fedb24f 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -262,4 +262,11 @@ return UIEdgeInsetsInsetRect(self.bounds, self.reactCompoundInsets); } +#pragma mark - Accessiblity + +- (UIView *)reactAccessibilityElement +{ + return self; +} + @end