Skip to content

Commit d4a788c

Browse files
committed
fix(iOS): reset other accessibility props on view recycle
Per @javache review feedback on #57196: extend prepareForRecycle to clear all accessibilityElement-side state that leaks across recycled views, not just accessibilityViewIsModal. Defaults sourced from AccessibilityProps.h; UIKit-side resets cover the props that updateProps writes via direct UIKit setters. Note: accessibilityRespondsToUserInteraction's C++ default is true, so the reset value is YES (not NO). Skipped: accessibilityActions / accessibilityLabelledBy / accessibilityLiveRegion have no direct UIKit-side state to reset.
1 parent 51c53b7 commit d4a788c

1 file changed

Lines changed: 31 additions & 5 deletions

File tree

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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -724,12 +724,38 @@ - (void)prepareForRecycle
724724
_reactSubviews = [NSMutableArray new];
725725
_layoutMetrics = {};
726726

727-
// Reset accessibilityViewIsModal so it does not leak across recycled views.
728-
// updateProps only writes this property when (oldProps != newProps); after a
729-
// view is recycled _props resets to the default (NO), so a recycled view that
730-
// is reused without the prop keeps the stale YES and traps VoiceOver / UI
731-
// automation against a now-unrelated subtree (empty accessibility tree).
727+
// Reset accessibility props so they do not leak across recycled views.
728+
// updateProps only writes these when (oldProps != newProps); after a view is
729+
// recycled _props resets to the AccessibilityProps default-constructed
730+
// values, so any UIKit-side state previously set stays stale unless cleared
731+
// here. The most visible failure is `accessibilityViewIsModal` /
732+
// `accessibilityElementsHidden` trapping VoiceOver against a now-unrelated
733+
// subtree (empty accessibility tree). Defaults below match
734+
// AccessibilityProps.h.
735+
self.accessibilityElement.isAccessibilityElement = NO;
736+
self.accessibilityElement.accessibilityLabel = nil;
737+
self.accessibilityElement.accessibilityLanguage = nil;
738+
self.accessibilityElement.accessibilityHint = nil;
739+
self.accessibilityElement.accessibilityValue = nil;
740+
self.accessibilityElement.accessibilityTraits = UIAccessibilityTraitNone;
732741
self.accessibilityElement.accessibilityViewIsModal = NO;
742+
self.accessibilityElement.accessibilityElementsHidden = NO;
743+
// Note: AccessibilityProps default for accessibilityRespondsToUserInteraction is `true`.
744+
self.accessibilityElement.accessibilityRespondsToUserInteraction = YES;
745+
self.accessibilityIgnoresInvertColors = NO;
746+
if ([self.accessibilityElement respondsToSelector:@selector(setAccessibilityIdentifier:)]) {
747+
((UIView *)self.accessibilityElement).accessibilityIdentifier = nil;
748+
} else {
749+
self.accessibilityIdentifier = nil;
750+
}
751+
#if !TARGET_OS_TV
752+
if (@available(iOS 13.0, *)) {
753+
self.showsLargeContentViewer = NO;
754+
self.largeContentTitle = nil;
755+
}
756+
#endif
757+
_accessibilityOrderNativeIDs = nil;
758+
self.accessibilityElements = nil;
733759
}
734760

735761
- (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet<NSString *> *_Nullable)props

0 commit comments

Comments
 (0)