Touch events do not work when multiple screens are active on Android
Closed this issue Β· 43 comments
I added a way to have transparent scenes in react-navigation a while back to support dialogs (https://github.com/react-navigation/react-navigation-stack/blob/master/src/views/StackView/StackViewCard.js#L35). It keeps all screens active in the stack but this seems to break touch events on Android because of the logic here: https://github.com/kmagiera/react-native-screens/blob/master/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.java#L189
The react-navigation code is basically this:
const modalNavigatorConfig = {
initialRouteName: 'app',
mode: 'modal',
headerMode: 'none',
transparentCard: true, // <- The option for transparent card
defaultNavigationOptions: {
gesturesEnabled: false,
},
transitionConfig: () => ({
transitionSpec: {
duration: 300,
easing: Easing.inOut(Easing.ease),
timing: Animated.timing,
},
screenInterpolator: sceneProps => {
const { position, scene } = sceneProps;
const { index } = scene;
const opacity = position.interpolate({
inputRange: [index - 1, index],
outputRange: [0, 1],
});
return { opacity };
},
}),
};
const ModalStack = createAppContainer(
createStackNavigator(
{
app: {
screen: RootStack,
},
{ screen: SomeOtherRoute },
},
modalNavigatorConfig,
),
);
+1 We recently upgraded to React Navigation 3.X and it looks like all 3.X compatible versions of react-native-screens have this issue. We use transparentCard for our dialog system as well.
@janicduplessis @jbacon-zynga
Any workaround?
Think I'm seeing a similar issue on iOS: when navigating back to a previous screen in a stack that has transparentCard set, the screen doesn't respond to touch events. Had a poke around with the Element Inspector and it seems that the component tree is "StackViewLayout > ScreenContainer" after navigating back, compared to "StackViewLayout > SceneView > [ Screen ]" initially. Navigation gestures still work, screen focus events still trigger and navigating from the screen via code is fine. Will investigate further and try to get a snack up and running asap
@GfxKai I'm running into the same issue exactly, yet I haven't been able to replicate it in a snack. I can confirm that the component tree ends in ScreenContainer
, and that toggling transparentCard
to false
on the StackNavigator fixes the component tree and resolves the unresponsiveness.
Yeah I couldn't reproduce in a snack either. Not sure why
I think the issue is that whenever a screen is added to a stack, the block here loops through all screens that are already mounted and active (usually only the screen immediately below the one being added to the top of the stack) and disables user interaction for the duration of the transition. This block checks for the end of the transition whenever a screen is added or removed (by waiting for there to only be 1 active screen) and then re-enables user interaction on the top-most screen.
Since (I think) the transparentCard prop keeps all screens in the stack active, once you add a third screen to a transparent stack, user interaction for the two screens underneath is disabled and never re-enabled again. Hence, when you pop that third screen, you are left with an unresponsive screen underneath.
Unfortunately I really don't know enough Obj-C to come up with a solution other than just allowing user interaction during transition π¬ I imagine we need some other way of detecting a finished transition other than checking the number of active screens. Perhaps it's possible to watch for a transition finish using onDidFocus on the js side then set a prop on the UIView over the bridge? π€·ββοΈ
react-native-screens disable touch interaction for the duration of screen transition (so that you can't interact with buttons on the screen that is going away). The way we detect screen transition now is when there are two or more active screens. I believe that is the case when you have a transaprent dialog too. This would need to be changed in screen implementation (both on Android and iOS)
This would need to be changed in screen implementation (both on Android and iOS)
Is this change possible in react-native-screens or it needs to be done in the respective OSes?
@ovy9086 - let's keep this all within this thread as it's the same underlying issue.
@ovy9086's other post below
When using react-native-screens with react-navigation 3 , enabling both
useScreens()
andtransparentCard
in thecreateStackNavigator
options causes weird issues.On iOS, if having more than 3 routes in the StackNavigator, when going back to the 2nd screen, you will not be able to interact the view anymore. Apparently something blocks it.
On Android, the 2nd screen is not clickable from the first time you navigate to it.Here is a repo to reproduce this : https://github.com/ovy9086/react-nav-transparent-card-bug
@kmagiera What was the reason for moving touch event handling inside rn-screens vs using the pointerEvents from JS that react-navigation sets like it was before?
react-native-screens disable touch interaction for the duration of screen transition [...] The way we detect screen transition now is when there are two or more active screens
This seems contradictory to the documentation:
It it possible to have as many active children as you'd like but in order for the component to be the most effictent we should keep the number of active screens to the minimum.
I found this commit caused a similar problem in the FluidTransitions package. Since that change was introduced in alpha-18, I use alpha-17 to work around the problem.
still no updates on this ? :)
react-native-screens disable touch interaction for the duration of screen transition (so that you can't interact with buttons on the screen that is going away). The way we detect screen transition now is when there are two or more active screens. I believe that is the case when you have a transaprent dialog too. This would need to be changed in screen implementation (both on Android and iOS)
I'm using:
Expo SDK 32
React Native Screens 1.0.0-alpha.19
React Navigation 3.0.9
And the screens are all frozen when I have a TabNavigator inside of a TabNavigator (this happened after upgrading from expo SDK 31 to 32)
I have this same issue, remove of useScreens(); fixed this problem
I'm also facing this issue.
Configuration
Expo SDK 32
React Native Screens imported from Expo
React Navigation 3.9.1
1 tab navigator with 4 tabs
To reproduce:
- Open app - tab 1 is open by default
- Navigate to tab 2 - everything on tab 2 is responsive as expected
- Navigate back to tab 1 - everything still responsive
- Navigate back to tab 2 - screen is completely unresponsive.
SceneView
+ children have been replaced byScreenContainer
-->RNSScreenContainer
Resolution
Remove react-native-screens
until the issue is fixed :(
Same issue.
Weird expo would include such an unstable library. Iβd say this issue will only become more common with people trying to improve performance.
Removing react-native-screens until a fix is added. So sad though.
We have the same problem with useScreens()
+ transparentCard: true
I have the same problem, too
Same here ... :(
Facing the same problem here. Will this be fixed with SDK 33?
Same problem here, removing react-native-screens solved the problem.
After upgrading to Expo 33, my original issue no longer occurred on iOS. I did however have a problem with nested ScrollViews/FlatListsβI could scroll them, but the list items were not tappable. I resolved that by adding keyboardShouldPersistTaps="always"
to the nested ScrollViews/FlatLists.
I'm still facing issues on Android
TouchableOpacity components are not responding to touch in any nested screens on Android when using transparentCard : true
within the createStackNavigator
config.
I did a little expirement and changed this line from Screen.java
:
@Override
public PointerEvents getPointerEvents() {
return mTransitioning ? PointerEvents.NONE : PointerEvents.AUTO;
}
to :
@Override
public PointerEvents getPointerEvents() {
return PointerEvents.AUTO;
}
This seems to resolve the issue as all the views will be able to receive touch events even when "transitioning" and I haven't seen any side effects when it comes to performance. I might make a PR with a config to override the default behaviour of this method...
any news on this one?
shouldn't this kind of stuff handled in navigation itself? using pointerEvents
prob for example.
I just wanted to mention this one more time, I think this kind of stuff should be implemented in navigation level (probably in js), it doesn't feel any good to have debouncing only with useScreens();
.
Same issue here useScreens() + transparentCard: true.
And if we set transparentCard as false, a white screen is rendered for a small moment before the modal its rendered
Anyone had the chance to look into if there is a similar solution in iOS for @emeraldsanto's changes?
I change the import of the StackNavigator from import { createStackNavigator } from 'react-navigation-stack';
to the one that say this library
import createNativeStackNavigator from 'react-native-screens/createNativeStackNavigator';
and it's working for me
@alexpareto I can look into it but according to my testing the events are still triggered on iOS without changing anything. I found the piece of code managing the touch events in RNSScreenContainer.m
:
// if we are down to one active screen it means the transitioning is over and we want to notify
// the transition has finished
if ((activeScreenRemoved || activeScreenAdded) && _activeScreens.count == 1) {
RNSScreenView *singleActiveScreen = [_activeScreens anyObject];
// restore interactions
singleActiveScreen.userInteractionEnabled = YES;
[singleActiveScreen notifyFinishTransitioning];
}
It seems to be pretty much the same code as Android, the difference is most likel in the calculation of active screens. I can check it out and report back here if I find anything.
@15matias15's solution (to use the in-package createNativeStackNavigator
) is the right solution for now, but it breaks when using react-navigation-stack
2.0 (currently in alpha). I opened #173 to track
@emeraldsanto's solution worked on Android
For iOS, I fixed it by disabling the logic which disables interactions during a transition. I did this by going to RNSScreenContainer.m and changed
screen.userInteractionEnabled = NO;
to
screen.userInteractionEnabled = YES;
Still looking for a solution that works on Android...
Any update on this? This is happening to me as well on iOS while using enableScreens();
and transparentCard: true
together.
This doesn't seem to be an issue for me anymore after upgrading to the following versions:
"react-native": "https://github.com/expo/react-native/archive/sdk-38.0.2.tar.gz", // or 0.62.2
"react-native-screens": "~2.9.0",
"react-navigation": "^4.0.10",
Hope that can help
I had same issue, reinstalling the react-native-screens
resolved the issue.
I'm running into this issue with RN Screens v3. I'm building a pager with RN Screens but once I want to have more than one page active at the same time, touches are not available anymore.
Only one Screen
component of ScreenContainer
can have activityState
with value 2
at the same time and that Screen
is responsive.
Thanks @WoLewicki, my use case is a large list where I would like to offload the parts of the list that are not visible.
The prototype has wraps a Screen component per Item but that means that only one Item is touchable. am I approaching this correctly? Or this is simply not a use case for react native screens?
I am afraid it is not the use case, at least not for now. react-native-screens
is meant to expose one Screen
component for all of the content you are interacting with at the moment, but you can play with performUpdate
method of ScreenContainer.java
and updateContainer
of ScreenContainer.m
to try and change the attaching/detaching logic according to your use case.