-
Notifications
You must be signed in to change notification settings - Fork 24.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Android/iOS Accessible Live Regions #34735
Comments
The functionality requires the creation of a queue for iOS announcements.
Current Tasks:
|
Further investigate announcementFinished APIThe functionality was implemented using ANNOUNCEMENT_DID_FINISH_EVENT as discussed on StackOverflow. react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.ios.js Lines 95 to 124 in cfe0032
|
Test UIAccessibilitySpeechAttributeQueueAnnouncement in react-native (StackOverflow)https://reactnative.dev/docs/next/accessibilityinfo#announceforaccessibilitywithoptions RCTAccessibilityManager
RCT_EXPORT_METHOD(announceForAccessibilityWithOptions
: (NSString *)announcement options
: (JS::NativeAccessibilityManager::SpecAnnounceForAccessibilityWithOptionsOptions &)options)
{
if (@available(iOS 11.0, *)) {
NSMutableDictionary<NSString *, NSNumber *> *attrsDictionary = [NSMutableDictionary new];
if (options.queue()) {
attrsDictionary[UIAccessibilitySpeechAttributeQueueAnnouncement] = @(*(options.queue()) ? YES : NO);
}
if (attrsDictionary.count > 0) {
NSAttributedString *announcementWithAttrs = [[NSAttributedString alloc] initWithString:announcement
attributes:attrsDictionary];
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcementWithAttrs);
} else {
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement);
}
} else {
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement);
}
} |
Test existing react-native accessibilityLiveRegion functionality on Android Paper
Testing Android accessibilityLiveRegion assertive (Paper)
<RNTesterBlock title="LiveRegion">
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<View accessibilityLiveRegion="polite">
<Text>
Announced {this.state.count} times with accessibilityLiveRegion
polite
</Text>
</View>
<View accessibilityLiveRegion="assertive">
<Text>
Second announcement {this.state.count} times with
accessibilityLiveRegion assertive
</Text>
</View>
</RNTesterBlock> https://www.icloud.com/iclouddrive/08b3HMFKoLEfpMCCyX9enjyIw#android_paper_assertive Testing Android accessibilityLiveRegion polite (Paper)
<RNTesterBlock title="LiveRegion">
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<View accessibilityLiveRegion="polite">
<Text>
Announced {this.state.count} times with accessibilityLiveRegion
polite
</Text>
</View>
<View accessibilityLiveRegion="polite">
<Text>
Second announcement {this.state.count} times with
accessibilityLiveRegion polite
</Text>
</View>
</RNTesterBlock> https://www.icloud.com/iclouddrive/078YVxbmyt9XswScPrBtTuGrg#android_paper_polite Takeaways on Android:
|
Implementation Design
<>
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<View accessibilityLiveRegion="polite">
<Text>
Announced {this.state.count} times with accessibilityLiveRegion
polite
</Text>
</View>
<View accessibilityLiveRegion="polite">
<Text>
Second announcement {this.state.count} times with
accessibilityLiveRegion polite
</Text>
</View>
</> |
Detect accessibility content changeThe accessibility content changes if one of the accessibility props (accessibilityLabel, hint, state or child props) changes. (example1, example2) accessibilityLabel
Tasks:
Commit 972a40a relevant notes
What are the conditions that may change the accessibility of content on AndroidThe parent component with accessible={true} should trigger new announcement (documentation and PR). The Diffing should be done on the parent component with accessible={true} Detecting accessibility announcement change in JavaScript
RCTRecursiveAccessibilityLabel already computes a new label by iterating over children's views. Logic should not be duplicated on JS side. |
Read documentation, investigate different solutions, plan work (details) -- Test existing react-native accessibilityLiveRegion functionality on Android Paper (details)Include Video recording of different scenarios (assertive, polite, none)Write down expected behaviour for iOS Implementation Design (details) Detect accessibility content change (details) Read documentation, investigate different solutions, plan work ([details](facebook#34735 (comment))) "Test existing react-native accessibilityLiveRegion functionality on Android Paper ([details](facebook#34735 (comment))) Include Video recording of different scenarios (assertive, polite, none) Write down expected behaviour for iOS" Implementation Design ([details](facebook#34735 (comment))) Detect accessibility content change ([details](facebook#34735 (comment)))
Automatic content groupingSolution proposted in PR #35432 does not work with automatic content grouping (for ex. child component Text change does not trigger a new announcement when focused on another component). Sourcecode of the example
function AccessibilityExpandedExample(): React.Node {
const [expand, setExpanded] = React.useState(false);
const [pressed, setPressed] = React.useState(false);
const expandAction = {name: 'expand'};
const collapseAction = {name: 'collapse'};
return (
<>
<RNTesterBlock title="Collapse/Expanded state change (Paper)">
<Button
onPress={() => {
setExpanded(!expand);
}}
title="click me to change state"
/>
<View
accessibilityLiveRegion="polite"
accessibilityRole="button"
accessible={true}
focusable={true}
nativeID={'1'}
style={{backgroundColor: 'red', height: 200, width: 400}}>
<Text>{expand ? null : 'my text'}</Text>
</View>
</RNTesterBlock>
</>
);
} The parent accessibility label is set to the value of Child attributedString.text. The value is changed when VoiceOver focuses on the component.
react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm Lines 701 to 726 in 53f7b76
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 138 to 141 in 53f7b76
While the prop diffing is triggered when the new prop is passed to the child component, as there is no change in the accessibilityLabel prop, no announcement is triggered here. react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm Lines 298 to 308 in 53f7b76
Nested Text receives props update here, an option would be checking for a change in the attributedtext.string react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 83 to 99 in 53f7b76
The logic behind the Text accessibilityElements seems more complex than other component as they can be nested with accessibilityRole="link", but the idea is that every class can over-ride the accessibilityLabel and other getter methods to change the way labels are generated react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 151 to 180 in 53f7b76
Android - Behaviour when child text changes
Draft solution fabOnReact@85b1c1f |
The state update triggers onFocus (when VoiceOver screenreader focus moves to the text). Mechanism behind ParagraphComponentView state update
react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h Lines 94 to 98 in 53f7b76
react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp Lines 100 to 115 in 53f7b76
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 101 to 106 in 53f7b76
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 60 to 64 in 53f7b76
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 138 to 141 in 53f7b76
|
Investigate how RCTMountingManager triggers state updates in RCTParagraphreact-native/React/Fabric/Mounting/RCTMountingManager.mm Lines 133 to 135 in 53f7b76
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 101 to 106 in 53f7b76
update state is called when Text.js children (the value of the text) changes CLICK TO OPEN EXAMPLE SOURCECODE
function AccessibilityExpandedExample(): React.Node {
const [expand, setExpanded] = React.useState(false);
const [pressed, setPressed] = React.useState(false);
const expandAction = {name: 'expand'};
const collapseAction = {name: 'collapse'};
return (
<>
<RNTesterBlock title="Collapse/Expanded state change (Paper)">
<Button
onPress={() => {
setExpanded(!expand);
}}
title="click me to change state"
/>
<View
accessibilityLiveRegion="polite"
accessibilityRole="button"
accessible={true}
focusable={true}
nativeID={'1'}
style={{backgroundColor: 'red', height: 200, width: 400}}>
<Text>{expand ? null : 'my text'}</Text>
</View>
</RNTesterBlock>
</>
);
}
|
Android - testing announcement of accessibilityState selected with accessibilityLiveRegion
ENABLE AUDIO 2022-11-25.17-10-34.mp4 |
iOS Text accessibilityLabel never announced with Fabric (main branch)cc @blavalla This is an iOS issue in the main branch, the Text accessibilityLabel is set to always equal the Text attributed.string react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm Lines 137 to 140 in 8b8f4f3
Android - Text correctly announcing accessibilityLabel
ENABLE AUDIO 2022-11-25.21-22-30.mp4iOS - not announcing accessibilityLabel
ENABLE AUDIO VID_20221125_212309.mp4 |
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days. |
This issue was closed because it has been stalled for 7 days with no activity. |
Description
The accessibilityLiveRegion prop is currently only supported on Android, and there are issues with it even on that platform. Ideally this prop would consistently work on both iOS and Android.
Expected Behavior
Whenever an element with accessibilityLiveRegion set, or any descendant of an element with accessibilityLiveRegion set has any content changes, they should be queued up to be announced by the screen reader. “Polite” announcements should not interrupt any ongoing announcements, while “assertive” ones should interrupt.
Android Details
On Android, the accessibilityLiveRegion prop works in the older Paper renderer, but not the newer Fabric renderer.
iOS Details
On iOS there is no concept of live regions at a system level, so to support this we’d need to detect when content changes on any element (or descendant) with the accessibilityLiveRegion prop set, and make a manual announcement. These announcements will have to be queued up with the UIAccessibilitySpeechAttributeQueueAnnouncement property set to either make them “polite” or “assertive”.
The behavior of exactly how the queue works (maximum length, what happens when new announcements are queued after that length, etc.) should match Android and web as closely as possible.
Version
0.66
Output of
npx react-native info
see description
Steps to reproduce
see description
Snack, code example, screenshot, or link to a repository
see description
The text was updated successfully, but these errors were encountered: