From 136d463569ee8d6ef49660c693aff0b0bf6e691d Mon Sep 17 00:00:00 2001 From: Maxence Henneron Date: Fri, 24 Oct 2025 17:48:03 -0700 Subject: [PATCH 1/2] [ios][accessibility] Fix action labels on iOS, fixes #53496 --- .../View/RCTViewComponentView.mm | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 29c543c58098..30557dbdb601 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -53,6 +53,7 @@ @implementation RCTViewComponentView { BOOL _useCustomContainerView; NSMutableSet *_accessibilityOrderNativeIDs; RCTSwiftUIContainerViewWrapper *_swiftUIWrapper; + NSDictionary *_accessibilityActionsLabelMap; } #ifdef RCT_DYNAMIC_FRAMEWORKS @@ -1495,14 +1496,24 @@ - (BOOL)shouldGroupAccessibilityChildren return nil; } + NSMutableDictionary *labelMap = [NSMutableDictionary new]; NSMutableArray *customActions = [NSMutableArray array]; for (const auto &accessibilityAction : accessibilityActions) { + NSString *actionName = RCTNSStringFromString(accessibilityAction.name); + NSString *actionLabel = actionName; + + if (accessibilityAction.label.has_value()) { + actionLabel = RCTNSStringFromString(accessibilityAction.label.value()); + labelMap[actionLabel] = actionName; + } + [customActions - addObject:[[UIAccessibilityCustomAction alloc] initWithName:RCTNSStringFromString(accessibilityAction.name) + addObject:[[UIAccessibilityCustomAction alloc] initWithName:actionLabel target:self selector:@selector(didActivateAccessibilityCustomAction:)]]; } + _accessibilityActionsLabelMap = [labelMap copy]; return [customActions copy]; } @@ -1553,7 +1564,13 @@ - (void)accessibilityDecrement - (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)action { if (_eventEmitter && _props->onAccessibilityAction) { - _eventEmitter->onAccessibilityAction(RCTStringFromNSString(action.name)); + // iOS defines the name as the localized label, so use our map to convert this back to the non-localized action name + // when passing to JS. This allows for standard action names across platforms. + NSString *actionName = action.name; + if (_accessibilityActionsLabelMap && _accessibilityActionsLabelMap[action.name]) { + actionName = _accessibilityActionsLabelMap[action.name]; + } + _eventEmitter->onAccessibilityAction(RCTStringFromNSString(actionName)); return YES; } else { return NO; From aefa122e295ff3e00ca1f8f848b90a8c9bc45647 Mon Sep 17 00:00:00 2001 From: Maxence Henneron Date: Thu, 20 Nov 2025 10:16:41 -0800 Subject: [PATCH 2/2] Accessibility action labels - reuse the props instead of building a name/label map --- .../ComponentViews/View/RCTViewComponentView.mm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 30557dbdb601..7ed3baaf122b 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -53,7 +53,6 @@ @implementation RCTViewComponentView { BOOL _useCustomContainerView; NSMutableSet *_accessibilityOrderNativeIDs; RCTSwiftUIContainerViewWrapper *_swiftUIWrapper; - NSDictionary *_accessibilityActionsLabelMap; } #ifdef RCT_DYNAMIC_FRAMEWORKS @@ -1496,7 +1495,6 @@ - (BOOL)shouldGroupAccessibilityChildren return nil; } - NSMutableDictionary *labelMap = [NSMutableDictionary new]; NSMutableArray *customActions = [NSMutableArray array]; for (const auto &accessibilityAction : accessibilityActions) { NSString *actionName = RCTNSStringFromString(accessibilityAction.name); @@ -1504,7 +1502,6 @@ - (BOOL)shouldGroupAccessibilityChildren if (accessibilityAction.label.has_value()) { actionLabel = RCTNSStringFromString(accessibilityAction.label.value()); - labelMap[actionLabel] = actionName; } [customActions @@ -1513,7 +1510,6 @@ - (BOOL)shouldGroupAccessibilityChildren selector:@selector(didActivateAccessibilityCustomAction:)]]; } - _accessibilityActionsLabelMap = [labelMap copy]; return [customActions copy]; } @@ -1564,11 +1560,15 @@ - (void)accessibilityDecrement - (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)action { if (_eventEmitter && _props->onAccessibilityAction) { - // iOS defines the name as the localized label, so use our map to convert this back to the non-localized action name - // when passing to JS. This allows for standard action names across platforms. + // iOS defines the name as the localized label, so iterate through accessibilityActions to find the matching + // non-localized action name when passing to JS. This allows for standard action names across platforms. NSString *actionName = action.name; - if (_accessibilityActionsLabelMap && _accessibilityActionsLabelMap[action.name]) { - actionName = _accessibilityActionsLabelMap[action.name]; + for (const auto &accessibilityAction : _props->accessibilityActions) { + if (accessibilityAction.label.has_value() && + [RCTNSStringFromString(accessibilityAction.label.value()) isEqualToString:action.name]) { + actionName = RCTNSStringFromString(accessibilityAction.name); + break; + } } _eventEmitter->onAccessibilityAction(RCTStringFromNSString(actionName)); return YES;