Skip to content

Commit 602a2d2

Browse files
maxencehenneronmeta-codesync[bot]
authored andcommitted
Fix action labels on iOS, fixes facebook#53496 (facebook#54286)
Summary: This is my PR 2 out of 3 to improve accessibility on iOS. If you're reading this, please also have a look at facebook#54131, this is a critical race condition bug that basically makes accessibility extremely hard to handle on large apps. Fix for facebook#53496: on the new architecture, any accessibility label is ignored and only the "name" is read by VoiceOver. We want to use name as an "action id", and label as the translatable value. While android has a id/label system for actions, iOS doesn't so my implementation, inspired by the old paper architecture, maps labels to name. Native iOS will only see the labels but the JS side will properly receive names when an action is executed. I believe this is the correct way of handling this. Another thing we could do is just deprecate "label" and expect name to be in the correct language, but I do not like that. ## Changelog: [IOS][FIXED] - Accessibility actions labels are not read by VoiceOver Pull Request resolved: facebook#54286 Test Plan: Open RN-Tester, go to Accessibility -> Accessibility Action Examples, enable VoiceOver and select "This view supports many actions". Now scroll up fast to select the actions. On the current implementation you will hear VoiceOver say "copy", then "cut", then "paste". It now says "cut label", "copy label", "paste label". As a reference, here's how this view is implemented: ```jsx <View accessible={true} accessibilityActions={[ {name: 'cut', label: 'cut label'}, {name: 'copy', label: 'copy label'}, {name: 'paste', label: 'paste label'}, ]} onAccessibilityAction={event => { switch (event.nativeEvent.actionName) { case 'cut': Alert.alert('Alert', 'cut action success'); break; case 'copy': Alert.alert('Alert', 'copy action success'); break; case 'paste': Alert.alert('Alert', 'paste action success'); break; } }}> <RNTesterText>This view supports many actions.</RNTesterText> </View> ``` Reviewed By: cipolleschi Differential Revision: D87766483 Pulled By: javache fbshipit-source-id: a979f0126efce4b87c6d823519d512229fb4118e
1 parent d0e9b9c commit 602a2d2

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,8 +1497,15 @@ - (BOOL)shouldGroupAccessibilityChildren
14971497

14981498
NSMutableArray<UIAccessibilityCustomAction *> *customActions = [NSMutableArray array];
14991499
for (const auto &accessibilityAction : accessibilityActions) {
1500+
NSString *actionName = RCTNSStringFromString(accessibilityAction.name);
1501+
NSString *actionLabel = actionName;
1502+
1503+
if (accessibilityAction.label.has_value()) {
1504+
actionLabel = RCTNSStringFromString(accessibilityAction.label.value());
1505+
}
1506+
15001507
[customActions
1501-
addObject:[[UIAccessibilityCustomAction alloc] initWithName:RCTNSStringFromString(accessibilityAction.name)
1508+
addObject:[[UIAccessibilityCustomAction alloc] initWithName:actionLabel
15021509
target:self
15031510
selector:@selector(didActivateAccessibilityCustomAction:)]];
15041511
}
@@ -1553,7 +1560,17 @@ - (void)accessibilityDecrement
15531560
- (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)action
15541561
{
15551562
if (_eventEmitter && _props->onAccessibilityAction) {
1556-
_eventEmitter->onAccessibilityAction(RCTStringFromNSString(action.name));
1563+
// iOS defines the name as the localized label, so iterate through accessibilityActions to find the matching
1564+
// non-localized action name when passing to JS. This allows for standard action names across platforms.
1565+
NSString *actionName = action.name;
1566+
for (const auto &accessibilityAction : _props->accessibilityActions) {
1567+
if (accessibilityAction.label.has_value() &&
1568+
[RCTNSStringFromString(accessibilityAction.label.value()) isEqualToString:action.name]) {
1569+
actionName = RCTNSStringFromString(accessibilityAction.name);
1570+
break;
1571+
}
1572+
}
1573+
_eventEmitter->onAccessibilityAction(RCTStringFromNSString(actionName));
15571574
return YES;
15581575
} else {
15591576
return NO;

0 commit comments

Comments
 (0)