-
-
Save louy/6b66c45ae47bb4e3bac5a104dd0649ff to your computer and use it in GitHub Desktop.
/** | |
* @author Louay Alakkad (github.com/louy) | |
* @license MIT https://opensource.org/licenses/MIT | |
*/ | |
import React from 'react' | |
import PropTypes from 'prop-types' | |
import { | |
NativeModules, | |
ViewProps, | |
ViewPropTypes, | |
findNodeHandle, | |
requireNativeComponent, | |
Platform, | |
View | |
} from 'react-native' | |
const { RNAccessibilityWrapperManager } = NativeModules | |
const RNAccessibilityWrapper = requireNativeComponent( | |
'RNAccessibilityWrapper' | |
) as React.ComponentClass<any> | |
interface AccessibilityWrapperProps extends ViewProps { | |
fieldsRefs?: React.RefObject<React.Component>[] | |
} | |
const AccessibilityWrapperPropTypes = { | |
...ViewPropTypes, | |
fieldsRefs: PropTypes.arrayOf(PropTypes.shape({ | |
current: PropTypes.object | |
}) as PropTypes.Validator<React.RefObject<React.Component>>) | |
} | |
class AccessibilityWrapperIOS extends React.Component< | |
AccessibilityWrapperProps | |
> { | |
public static propTypes = AccessibilityWrapperPropTypes | |
private ref = React.createRef<React.Component<any>>() | |
public componentDidMount() { | |
if (this.props.fieldsRefs) { | |
this.setAccessibilityFields(this.props.fieldsRefs.map(ref => ref.current)) | |
} | |
} | |
public componentDidUpdate() { | |
if (this.props.fieldsRefs) { | |
this.setAccessibilityFields(this.props.fieldsRefs.map(ref => ref.current)) | |
} | |
} | |
private setAccessibilityFields = ( | |
fields: (React.Component<any> | null)[] | |
) => { | |
const fieldTags = | |
fields && fields.map(component => component && findNodeHandle(component)) | |
return RNAccessibilityWrapperManager.setAccessibilityFields( | |
findNodeHandle(this.ref.current), | |
fieldTags | |
) | |
} | |
public render() { | |
return <RNAccessibilityWrapper {...this.props} ref={this.ref} /> | |
} | |
} | |
const AccessibilityWrapperAndroid: React.FunctionComponent< | |
AccessibilityWrapperProps | |
> = ({ fieldsRefs, ...props }) => <View {...props} /> | |
AccessibilityWrapperAndroid.propTypes = AccessibilityWrapperPropTypes | |
/** | |
* The AccessibilityWrapper component allows you to adjust the behaviour of the native platform | |
* when it comes to accessibility. Using this component you can tell the native platform to | |
* group all subviews together for accessibility purposes (since it's not always done by | |
* default), and you can even override the focus order of subviews | |
* | |
* @example | |
* export default class App extends Component<{}> { | |
* fooRef = React.createRef<Text>(); | |
* barRef = React.createRef<Text>(); | |
* bazRef = React.createRef<Text>(); | |
* | |
* public render() { | |
* return ( | |
* <AccessibilityWrapper fieldsRefs={[ | |
* this.barRef, | |
* this.fooRef, | |
* this.bazRef, | |
* ]}> | |
* <SafeAreaView> | |
* <Text ref={this.fooRef}>Foo</Text> | |
* <Text ref={this.barRef}>Bar</Text> | |
* <Text ref={this.bazRef}>Baz</Text> | |
* </SafeAreaView> | |
* </AccessibilityWrapper> | |
* ); | |
* } | |
* } | |
*/ | |
export default Platform.select<React.ComponentType<AccessibilityWrapperProps>>({ | |
ios: AccessibilityWrapperIOS as React.ComponentType< | |
AccessibilityWrapperProps | |
>, | |
android: AccessibilityWrapperAndroid as React.ComponentType< | |
AccessibilityWrapperProps | |
> | |
}) |
// | |
// RNAccessibilityWrapper.h | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <UIKit/UIKit.h> | |
#import <UIKit/UIAccessibilityContainer.h> | |
#import <React/RCTView.h> | |
@interface RNAccessibilityWrapper : RCTView | |
- (void) setAccessibilityFields: (NSArray *)reactTags; | |
@end |
// | |
// RNAccessibilityWrapper.m | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <Foundation/Foundation.h> | |
#import "RNAccessibilityWrapper.h" | |
#import <UIKit/UIKit.h> | |
@implementation RNAccessibilityWrapper | |
- (void) setAccessibilityFields: (NSArray *)fields | |
{ | |
NSMutableArray *accessibleElements = [NSMutableArray arrayWithCapacity:[fields count]]; | |
[fields enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { | |
UIView *field = obj; | |
[accessibleElements addObject:field]; | |
}]; | |
self.accessibilityElements = (NSArray *)accessibleElements; | |
} | |
- (bool) shouldGroupAccessibilityChildren { | |
return YES; | |
} | |
@end |
// | |
// RNAccessibilityViewManager | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <React/RCTViewManager.h> | |
@interface RNAccessibilityWrapperManager : RCTViewManager | |
@end |
// | |
// RNAccessibilityWrapper.m | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <Foundation/Foundation.h> | |
#import <React/RCTUIManager.h> | |
#import "RNAccessibilityWrapper.h" | |
#import "RNAccessibilityWrapperManager.h" | |
@implementation RNAccessibilityWrapperManager | |
RCT_EXPORT_MODULE() | |
- (UIView *)view { | |
return [[RNAccessibilityWrapper alloc] init]; | |
} | |
RCT_EXPORT_METHOD(setAccessibilityFields:(nonnull NSNumber *)reactTag | |
fieldsReactTags: (nonnull NSArray *)fieldsReactTags) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
RNAccessibilityWrapper *component = (RNAccessibilityWrapper *)[self.bridge.uiManager viewForReactTag:reactTag]; | |
NSMutableArray *fields = [NSMutableArray arrayWithCapacity:[fieldsReactTags count]]; | |
[fieldsReactTags enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { | |
NSNumber *tag = (NSNumber *)obj; | |
UIView *field = [self.bridge.uiManager viewForReactTag:tag]; | |
[fields addObject:field]; | |
}]; | |
[component setAccessibilityFields: fields]; | |
}); | |
} | |
@end |
Hello @louy
I am facing a problem with the custom order if the View renders like this in AccessbilityWrapper :
Bar
Baz
Foo
but voice over order should be like this
Bar
Foo
Baz
I am changing the order of fieldsRefs but not working as expected. It is the same as render
Please help me with this
Is there an equivalent for Android as well?
Android didn't have voice over controls when I did this, but things might have changed now, IDK sadly
@rahulpunchh I'm no longer maintaining this I'm afraid, so I can't help, but you can try asking on stackoverflow or somewhere similar
Hi, tnx for your work man. Probably it's why I can fix bugs.
I wrote a lib, it can be usefull I guess https://www.npmjs.com/package/react-native-a11y (NewArch, Android, iOs ) supported
Hi, tnx for your work man. Probably it's why I can fix bugs. I wrote a lib, it can be usefull I guess https://www.npmjs.com/package/react-native-a11y (NewArch, Android, iOs ) supported
Nice @ArturKalach !! Will check this out. Focus order has long been a pain point with RN and some layouts.
Is there an equivalent for Android as well?