mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: This PR fixes https://github.com/facebook/react-native/issues/30891 This PR is going to add an `accessibilityLanguage` prop to all the available components. This props is currently working only on iOS and should follow the [guidelines of the relative configuration](https://developer.apple.com/documentation/objectivec/nsobject/1615192-accessibilitylanguage). I'm in no way an expert on native programming (especially Objective-C) so I'm open to changes / improvements 🙂 ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [iOS] [Added] - Integrated the `accessibilityLanguage` prop to all the available components. The prop is available for any platform but it will work only on iOS. Pull Request resolved: https://github.com/facebook/react-native/pull/33090 Test Plan: This has been tested using both the Simulator, checking for the `Language` attribute, and using a physical device with the Voice Over enabled. <img width="1083" alt="Screenshot 2022-02-11 at 13 17 32" src="https://user-images.githubusercontent.com/5963683/153590415-65fcb4ff-8f31-4a0f-90e5-8eb1aae6aa3d.png"> Reviewed By: philIip Differential Revision: D34523608 Pulled By: rh389 fbshipit-source-id: b5d77fc0b3d76ea8ed8f30c8385459ba98122ff6
182 lines
9.1 KiB
Plaintext
182 lines
9.1 KiB
Plaintext
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RCTParagraphComponentAccessibilityProvider.h"
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <react/renderer/components/text/ParagraphProps.h>
|
|
#import <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
|
|
#import <react/renderer/textlayoutmanager/RCTTextLayoutManager.h>
|
|
#import <react/renderer/textlayoutmanager/TextLayoutManager.h>
|
|
|
|
#import "RCTAccessibilityElement.h"
|
|
#import "RCTConversions.h"
|
|
#import "RCTFabricComponentsPlugins.h"
|
|
#import "RCTLocalizationProvider.h"
|
|
|
|
using namespace facebook::react;
|
|
|
|
@implementation RCTParagraphComponentAccessibilityProvider {
|
|
NSMutableArray<UIAccessibilityElement *> *_accessibilityElements;
|
|
AttributedString _attributedString;
|
|
RCTTextLayoutManager *_layoutManager;
|
|
ParagraphAttributes _paragraphAttributes;
|
|
CGRect _frame;
|
|
__weak UIView *_view;
|
|
}
|
|
|
|
- (instancetype)initWithString:(facebook::react::AttributedString)attributedString
|
|
layoutManager:(RCTTextLayoutManager *)layoutManager
|
|
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
|
frame:(CGRect)frame
|
|
view:(UIView *)view
|
|
{
|
|
if (self = [super init]) {
|
|
_attributedString = attributedString;
|
|
_layoutManager = layoutManager;
|
|
_paragraphAttributes = paragraphAttributes;
|
|
_frame = frame;
|
|
_view = view;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSArray<UIAccessibilityElement *> *)accessibilityElements
|
|
{
|
|
if (_accessibilityElements) {
|
|
return _accessibilityElements;
|
|
}
|
|
|
|
__block NSInteger numberOfLinks = 0;
|
|
__block NSInteger numberOfButtons = 0;
|
|
__block NSString *truncatedText;
|
|
// build an array of the accessibleElements
|
|
NSMutableArray<UIAccessibilityElement *> *elements = [NSMutableArray new];
|
|
|
|
NSString *accessibilityLabel = _view.accessibilityLabel;
|
|
if (accessibilityLabel.length == 0) {
|
|
accessibilityLabel = RCTNSStringFromString(_attributedString.getString());
|
|
}
|
|
// add first element has the text for the whole textview in order to read out the whole text
|
|
RCTAccessibilityElement *firstElement =
|
|
[[RCTAccessibilityElement alloc] initWithAccessibilityContainer:_view.superview];
|
|
firstElement.isAccessibilityElement = YES;
|
|
firstElement.accessibilityTraits = _view.accessibilityTraits;
|
|
firstElement.accessibilityLabel = accessibilityLabel;
|
|
firstElement.accessibilityLanguage = _view.accessibilityLanguage;
|
|
firstElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view);
|
|
[firstElement setAccessibilityActivationPoint:CGPointMake(
|
|
firstElement.accessibilityFrame.origin.x + 1.0,
|
|
firstElement.accessibilityFrame.origin.y + 1.0)];
|
|
[elements addObject:firstElement];
|
|
|
|
// add additional elements for those parts of text with embedded link so VoiceOver could specially recognize links
|
|
|
|
[_layoutManager getRectWithAttributedString:_attributedString
|
|
paragraphAttributes:_paragraphAttributes
|
|
enumerateAttribute:RCTTextAttributesAccessibilityRoleAttributeName
|
|
frame:_frame
|
|
usingBlock:^(CGRect fragmentRect, NSString *_Nonnull fragmentText, NSString *value) {
|
|
if ([fragmentText isEqualToString:firstElement.accessibilityLabel]) {
|
|
// The fragment is the entire paragraph. This is handled as `firstElement`.
|
|
return;
|
|
}
|
|
if ((![value isEqualToString:@"button"] && ![value isEqualToString:@"link"])) {
|
|
return;
|
|
}
|
|
if ([value isEqualToString:@"button"] &&
|
|
([fragmentText isEqualToString:@"See Less"] ||
|
|
[fragmentText isEqualToString:@"See More"])) {
|
|
truncatedText = fragmentText;
|
|
return;
|
|
}
|
|
RCTAccessibilityElement *element =
|
|
[[RCTAccessibilityElement alloc] initWithAccessibilityContainer:self->_view];
|
|
element.isAccessibilityElement = YES;
|
|
if ([value isEqualToString:@"link"]) {
|
|
element.accessibilityTraits = UIAccessibilityTraitLink;
|
|
numberOfLinks++;
|
|
} else if ([value isEqualToString:@"button"]) {
|
|
element.accessibilityTraits = UIAccessibilityTraitButton;
|
|
numberOfButtons++;
|
|
}
|
|
element.accessibilityLabel = fragmentText;
|
|
element.frame = fragmentRect;
|
|
[elements addObject:element];
|
|
}];
|
|
|
|
if (numberOfLinks > 0 || numberOfButtons > 0) {
|
|
__block NSInteger indexOfLink = 1;
|
|
__block NSInteger indexOfButton = 1;
|
|
[elements enumerateObjectsUsingBlock:^(UIAccessibilityElement *element, NSUInteger idx, BOOL *_Nonnull stop) {
|
|
if (idx == 0) {
|
|
return;
|
|
}
|
|
if (element.accessibilityTraits & UIAccessibilityTraitLink) {
|
|
NSString *test = [RCTLocalizationProvider RCTLocalizedString:@"Link %ld of %ld."
|
|
withDescription:@"index of the link"];
|
|
element.accessibilityHint = [NSString stringWithFormat:test, (long)indexOfLink++, (long)numberOfLinks];
|
|
} else {
|
|
element.accessibilityHint =
|
|
[NSString stringWithFormat:[RCTLocalizationProvider RCTLocalizedString:@"Button %ld of %ld."
|
|
withDescription:@"index of the button"],
|
|
(long)indexOfButton++,
|
|
(long)numberOfButtons];
|
|
}
|
|
}];
|
|
}
|
|
|
|
if (numberOfLinks > 0 && numberOfButtons > 0) {
|
|
firstElement.accessibilityHint =
|
|
[RCTLocalizationProvider RCTLocalizedString:@"Links and buttons are found, swipe to move to them."
|
|
withDescription:@"accessibility hint for links and buttons inside text"];
|
|
|
|
} else if (numberOfLinks > 0) {
|
|
NSString *firstElementHint = (numberOfLinks == 1)
|
|
? [RCTLocalizationProvider RCTLocalizedString:@"One link found, swipe to move to the link."
|
|
withDescription:@"accessibility hint for one link inside text"]
|
|
: [NSString stringWithFormat:[RCTLocalizationProvider
|
|
RCTLocalizedString:@"%ld links found, swipe to move to the first link."
|
|
withDescription:@"accessibility hint for multiple links inside text"],
|
|
(long)numberOfLinks];
|
|
firstElement.accessibilityHint = firstElementHint;
|
|
|
|
} else if (numberOfButtons > 0) {
|
|
NSString *firstElementHint = (numberOfButtons == 1)
|
|
? [RCTLocalizationProvider RCTLocalizedString:@"One button found, swipe to move to the button."
|
|
withDescription:@"accessibility hint for one button inside text"]
|
|
: [NSString stringWithFormat:[RCTLocalizationProvider
|
|
RCTLocalizedString:@"%ld buttons found, swipe to move to the first button."
|
|
withDescription:@"accessibility hint for multiple buttons inside text"],
|
|
(long)numberOfButtons];
|
|
firstElement.accessibilityHint = firstElementHint;
|
|
}
|
|
|
|
if (truncatedText && truncatedText.length > 0) {
|
|
firstElement.accessibilityHint = (numberOfLinks > 0 || numberOfButtons > 0)
|
|
? [NSString
|
|
stringWithFormat:@"%@ %@",
|
|
firstElement.accessibilityHint,
|
|
[RCTLocalizationProvider
|
|
RCTLocalizedString:[NSString stringWithFormat:@"Double tap to %@.", truncatedText]
|
|
withDescription:@"accessibility hint for truncated text with links or buttons"]]
|
|
: [RCTLocalizationProvider RCTLocalizedString:[NSString stringWithFormat:@"Double tap to %@.", truncatedText]
|
|
withDescription:@"accessibility hint for truncated text"];
|
|
}
|
|
|
|
// add accessible element for truncation attributed string for automation purposes only
|
|
_accessibilityElements = elements;
|
|
return _accessibilityElements;
|
|
}
|
|
|
|
- (BOOL)isUpToDate:(facebook::react::AttributedString)currentAttributedString
|
|
{
|
|
return currentAttributedString == _attributedString;
|
|
}
|
|
|
|
@end
|