Accessibility focus event in React Native - react-native

I'm programming a react native game app for blind kids to help them with maths. There is a game in which they have to count how many animals are in the screen; when an animal is pressed it emits a sound.
In react native there is the onPress property of <TouchableWithouFeedback /> which allows me to play the sound, but when it comes to visually impaired users I have to announce that there is an animal instead of just playing the sound.
How can I know if a certain View is focused by the screen reader and call a function to do that?

There doesn't seem to be any way to react to the screen reader focusing on a particular object. Instead, you need to use the accessibilityLabel property on each animal object.
<TouchableOpacity accessible={true} accessibilityLabel="This is a tiger">
...
</TouchableOpacity>
When the user selects this object with say, a single tap, they will hear "This is a tiger." Then, after double-tapping the screen, they should hear the associated sound that all other users would normally hear.
I don't think there's really much more you can do than this with the given APIs. Not sure if the limitations are at the OS SDK or React Native level.
Check out the React Native docs on Accessibility for further details.

There's no way to detect, currently, if an element has VoiceOver or TalkBack focus. (I doesn't implement UIAccessibilityFocus neither TYPE_VIEW_ACCESSIBILITY_FOCUSED for Android

The only way to solve this is by developing a native module for images adding native listeners for accessibility events. This means in Android for example:
public void installAccessibilityDelegate() {
setAccessibilityDelegate(new AccessibilityDelegate() {
#Override
public boolean onRequestSendAccessibilityEvent(ViewGroup viewGroup, View child, AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
sendReactNativeEvent("start");
return false;
}
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED) {
sendReactNativeEvent("end");
return false;
}
return super.onRequestSendAccessibilityEvent(viewGroup, child, event);
}
});
}
My group developed a ios/android component which exposes those events for an image which is currently distributed via npm: https://www.npmjs.com/package/react-native-accessible-image

Related

React Navigation: How to share data between main screen and modals

Working on react native with react navigation, building an app.
There is a topic screen with a complex data object, and using React Navigation's Modal to show some parts of data, separately. The problem is when the user navigates to a modal screen, I have to pass the topic id as param and fetch topic data from the server once again, and because of the nature of data, users may open and close modals several times at a time.
Couldn't find a solution to share data from the parent screen to its modals, and I don't want to share data with State and Context. So is there another way to pass data to modal screen without using param or hooks?
Have you tried navigating to the modal like that :
navigation.navigate("YourModalName", {
topic: yourTopicObject,
},
});
You should be able to use the topic object in your modal like that :
class YourModalName extends Component {
componentDidMount() {
console.log(this.props.topic); // Accessing object in props.topic
}
}

How to determine mouse events in React Native (mobile)?

I need to use a callback function for the mouse click event. Below code is working in React web app:
const checkAnswer = (e: React.MouseEvent<HTMLButtonElement>) => {
const answer = e.currentTarget.value // here .value property is undefined and gives error in React-Native. Just e.currentTarget is defined.
const correct = questions[number].correct_answer === answer
if(correct) setScore(prev => prev + 1)
}
But I couldn't apply it on React Native (mobile) for Pressable or TouchableOpacity.
This is the render part of React web app's code:
<button value={answer} onClick{checkAnswer} />
and I try to apply it on React Native. The value will be passed to button's value. But there is no "value" option in native's Pressable component. Therefore I am confused
Any help?
you can determine touch events by following
<View onTouchStart={(e) => {console.log('touchMove',e.nativeEvent)}} />
Here is some similar scenario what I could able to understand from the question.
There is a Score state which will store the user's score of a quiz game.
There is a array of question where all the options and correct option are given.
There will be a button for the option, if user choose a option, and based on that it will validate if user is correct or not.
It might not be the exact scenario of you. But this could definitely help to solve your scenario.
Here is a solution for the scenario => solution.
If you click on the solution link you will be redirect to a page which is similar to the screenshot attached below. On very right you will find "My Device", "Android", "ios", choose any and run. Run via "my device" with expo app installed in your mobile if you don't want to be in queue for some certain time.
Note: from your code this is for React web:
<button value={answer} onClick{checkAnswer} />
but in React Native you have to use a click event like this:
<TouchableOpacity onPress={()=>buttonHandler()}></TouchableOpacity>
Here ()=> and () extra need to be added to work a function properly.
Also instead of .map() function you can use FlatList to improve your app. FlatList support lazy loading.

Native onKeyDown not firing in Modal for React Native Android

If, in my React Native application, I have some Java code like this:
public class MainActivity extends ReactActivity {
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KEYCODE_BUTTON_16) {
rfidScanning.startScanning();
return true;
} else {
return super.onKeyDown(keyCode, event);
}
}
The contents of onKeyDown will be called unless I have a React Native Modal component visible.
Why does the modal visibility change this, and how can I make it fire anyway or do something equivalent (such as capturing the event somewhere else in my Java code)?
Actually I think I found the problem. It looks to me like the relevant modal code is here:
https://github.com/facebook/react-native/blob/b531612b2c917e1f2bd6bb37bf854fe51e658b36/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java#L221
They actually handle the onKeyUp and not the onKeyDown. I confirmed this by verifying in my own code that onKeyUp fires with the modal open, but onKeyDown does not.
Wow.
I created an issue document for the bug here:
https://github.com/facebook/react-native/issues/32827

Keyboard Handling in React Native

How to make your app respond gracefully on keyboard appearance?
So far I have tried keyboard-aware-scroll, keyboardspacer and keyboard Avoiding view
Keyboard avoiding view didn't help at all I have tried it several times but it doesn't even respond to keyboard appearance.
Keyboardspacer gracefully works but in many cases it destroys the whole UI by crushing other view
keyboardaware scroll works when there is no scroll in the app but for long forms it doesn't work.
android:windowSoftInputMode="adjustPan" only works for android
What are the other options that we have for the app to gracefully respond when keyboard appears.
What do you use in your apps?
If none of these libraries does what you need, you can adjust your view manually by using the Keyboard module (docs at https://facebook.github.io/react-native/docs/keyboard)
With it you can react when you know a keyboard opens or closes, like so:
import * as React from 'react';
import { Keyboard } from 'react-native';
class MyComponent extends React.Component {
componentDidMount() {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow);
}
componentWillUnmount() {
this.keyboardDidHideListener.remove();
this.keyboardDidShowListener.remove();
}
keyboardDidShow = () => {
//Fix your view for when a keyboard shows
};
keyboardDidHide = () => {
//Fix your view for when a keyboard hides
};
//Rest of component...
}
For my projects I use react-native-keyboard-aware-scroll-view as well as KeyboardAvoidingView (try to play with behavior prop, it depends on your styling).
Take a look in Android configuration section in docs of react-native-keyboard-aware-scroll-view. I think it's something that you're looking for.
You can find following usefull answer related your question.
Q.How to change the Softkeyboard “Enter” button Text in android?
https://stackoverflow.com/a/53098939/6477946
Q. How to close or hide SoftKeyBoard
https://stackoverflow.com/a/53077131/6477946

How to navigate to a different view that is not a child?

I am new to react native and new to iOS (not programming) so please excuse me if this question is a simple one. I am trying to navigate from one view to another (with a transition), however they are not related so I do not need the back navigation. I actually do not have a navigation bar at all. When using the Navigator component it seems to not support this at all. I am not sure if there is a separate way to do this but I am not able to figure it out without implementing my own hack.
If I use the navigator component and keep pushing on the views then it just keeps them all in memory and I do not want that. I can transition from one view to another and then pop but I may end up going to the wrong view in that case. I can also replace the view but it seems that does not allow for transitions.
To give you a scenario think of it like this:
Application starts and loads a "Loading" screen.
When initial loading is complete it will then go to the "Login" screen.
There is a button on the "Login" screen to "Register" or "Retrieve Password".
If they click "Register" it will take them there with a button back to "Login".
If they click "Retrieve Password" it will take them to a page with buttons to go back to "Login" or "Register".
So by this example you can see that there is no way to pop because if you were on the login screen and went to the register screen and then wanted to go the retrieve password screen then pop just simply wouldn't work. I do not want any navigation controls on the screen I just want to be able to do a smooth transition between these screens.
Now I was able to find a way to do this but I had to add a method to the Navigator class and hack code in using some of there core methods which seems like its not a good idea at all but here is the code (note this is really just a hack to see if it would work):
Navigator.prototype.pushWithUnmount = function(route) {
var activeLength = this.state.presentedIndex + 1;
var activeStack = this.state.routeStack.slice(0, activeLength);
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
var nextStack = activeStack.concat([route]);
var destIndex = nextStack.length - 1;
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route),
]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(
destIndex,
null, // default velocity
null, // no spring jumping
() => {
this.replaceAtIndex(nextStack[destIndex], 0);
this.setState({
presentedIndex: 0,
});
}
);
});
}
By using the code provided above I am now able to do:
this.props.navigator.pushWithUnmount({ component: SomeComponent });
With this code the views are pushed onto the stack with a transition and the old views are unmounted when its finished.
Please tell me that I am doing something wrong and that there is a better way to do this?
The default router with React Native is pretty limited. I'd check out React Native Router Flux. We just switched to it a few weeks ago in our product and have really liked it. It does exactly what you want.