Skip to content
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

Touch events do not work when multiple screens are active on Android #61

Closed
janicduplessis opened this issue Jan 17, 2019 · 43 comments · Fixed by #624
Closed

Touch events do not work when multiple screens are active on Android #61

janicduplessis opened this issue Jan 17, 2019 · 43 comments · Fixed by #624
Labels
Bug Something isn't working

Comments

@janicduplessis
Copy link
Contributor

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,
  ),
);
@jbacon-zynga
Copy link

+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.

@zivchen
Copy link

zivchen commented Feb 7, 2019

@janicduplessis @jbacon-zynga
Any workaround?

@GfxKai
Copy link

GfxKai commented Feb 7, 2019

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

@zachgibb
Copy link

zachgibb commented Feb 7, 2019

@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.

@GfxKai
Copy link

GfxKai commented Feb 8, 2019

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? 🤷‍♂️

@kmagiera
Copy link
Member

kmagiera commented Mar 7, 2019

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)

@truted
Copy link

truted commented Mar 7, 2019

@kmagiera

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
Copy link

ovy9086 commented Mar 8, 2019

if somebody wants a quick repo to see this I added it here : #79

@brentvatne
Copy link
Collaborator

@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() and transparentCard in the createStackNavigator 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

@janicduplessis
Copy link
Contributor Author

@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?

@olegbl
Copy link

olegbl commented Mar 20, 2019

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.

@jkolyer
Copy link

jkolyer commented Mar 28, 2019

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.

@brentvatne
Copy link
Collaborator

@olegbl - indeed it does seem that the readme has gone out of sync with changes in the project, such is the life of an alpha version i suppose ;) @kmagiera has been quite busy recently, but perhaps in a couple weeks he can get back to it

@ovy9086
Copy link

ovy9086 commented Apr 19, 2019

still no updates on this ? :)

@jakejyang
Copy link

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)

@marcinj1
Copy link

marcinj1 commented May 1, 2019

I have this same issue, remove of useScreens(); fixed this problem

@brettdh
Copy link

brettdh commented May 1, 2019

I'm using the same workaround as @marcinj1, but it makes me sad, of course. 😔

@filipef101
Copy link

That is not a work arround @marcinj1 and @brettdh that means that you don't make use of this package.
It's sad that it basically is broken because of this and I don't see why it is inclued in expo when it clearly is not stable

@nhuesmann
Copy link

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:

  1. Open app - tab 1 is open by default
  2. Navigate to tab 2 - everything on tab 2 is responsive as expected
  3. Navigate back to tab 1 - everything still responsive
  4. Navigate back to tab 2 - screen is completely unresponsive. SceneView + children have been replaced by ScreenContainer --> RNSScreenContainer

Resolution

Remove react-native-screens until the issue is fixed :(

@travislang
Copy link

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.

satya164 added a commit to react-navigation/reanimated-stacks that referenced this issue May 19, 2019
This seems super-buggy atm.

On Android, initially this seems to work, but as soon as you start swiping and then release, no gestures such as touch work after that.
Same issue can also be reproduced if you just leave all screens active. Seems to be software-mansion/react-native-screens#61

On iOS, I get `undefined is not an object (evaluating 'this._ref.setNativeProps')`.
The `ref` is undefined for some reason only on iOS: https://github.com/kmagiera/react-native-screens/blob/acf80e640c584bf4019cfaf9356ee44b09e7dc99/src/screens.native.js#L44

If you do `active={focused ? 1 : 0}`, it works as expected on Android (though not desirable).
However, on iOS, no gestures such as touch seem to work.
satya164 added a commit to react-navigation/reanimated-stacks that referenced this issue May 19, 2019
This seems super-buggy atm.

On Android, initially this seems to work, but as soon as you start swiping and then release, no gestures such as touch work after that.
Same issue can also be reproduced if you just leave all screens active. Seems to be software-mansion/react-native-screens#61

On iOS, I get `undefined is not an object (evaluating 'this._ref.setNativeProps')`.
The `ref` is undefined for some reason only on iOS: https://github.com/kmagiera/react-native-screens/blob/acf80e640c584bf4019cfaf9356ee44b09e7dc99/src/screens.native.js#L44

If you do `active={focused ? 1 : 0}`, it works as expected on Android (though not desirable).
However, on iOS, no gestures such as touch seem to work.
satya164 added a commit to react-navigation/reanimated-stacks that referenced this issue May 19, 2019
This seems super-buggy atm.

On Android, initially this seems to work, but as soon as you start swiping and then release, no gestures such as touch work after that.
Same issue can also be reproduced if you just leave all screens active. Seems to be software-mansion/react-native-screens#61

On iOS, I get `undefined is not an object (evaluating 'this._ref.setNativeProps')`.
The `ref` is undefined for some reason only on iOS: https://github.com/kmagiera/react-native-screens/blob/acf80e640c584bf4019cfaf9356ee44b09e7dc99/src/screens.native.js#L44

If you do `active={focused ? 1 : 0}`, it works as expected on Android (though not desirable).
However, on iOS, no gestures such as touch seem to work.
satya164 added a commit to react-navigation/reanimated-stacks that referenced this issue May 19, 2019
This seems super-buggy atm.

On Android, initially this seems to work, but as soon as you start swiping and then release, no gestures such as touch work after that.
Same issue can also be reproduced if you just leave all screens active. Seems to be software-mansion/react-native-screens#61

On iOS, I get `undefined is not an object (evaluating 'this._ref.setNativeProps')`.
The `ref` is undefined for some reason only on iOS: https://github.com/kmagiera/react-native-screens/blob/acf80e640c584bf4019cfaf9356ee44b09e7dc99/src/screens.native.js#L44

If you do `active={focused ? 1 : 0}`, it works as expected on Android (though not desirable).
However, on iOS, no gestures such as touch seem to work.
@Sebastien-Lampazona
Copy link

Same here ... :(

@gitteraz
Copy link

Facing the same problem here. Will this be fixed with SDK 33?

@landorid
Copy link

landorid commented Jun 3, 2019

Same problem here, removing react-native-screens solved the problem.

@nhuesmann
Copy link

nhuesmann commented Jun 14, 2019

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.

@emeraldsanto
Copy link

emeraldsanto commented Aug 27, 2019

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...

@sijad
Copy link

sijad commented Aug 29, 2019

any news on this one?

shouldn't this kind of stuff handled in navigation itself? using pointerEvents prob for example.

@sijad
Copy link

sijad commented Sep 12, 2019

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();.

@15matias15
Copy link

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

@alexpareto
Copy link

Anyone had the chance to look into if there is a similar solution in iOS for @emeraldsanto's changes?

@15matias15
Copy link

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

@emeraldsanto
Copy link

@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.

@Ashoat
Copy link

Ashoat commented Oct 8, 2019

@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

@vtam311
Copy link

vtam311 commented Oct 18, 2019

@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;

@hugoh59
Copy link

hugoh59 commented Dec 16, 2019

Still looking for a solution that works on Android...

@talon-himself
Copy link

Any update on this? This is happening to me as well on iOS while using enableScreens(); and transparentCard: true together.

@clems36
Copy link

clems36 commented Aug 6, 2020

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

@WoLewicki WoLewicki linked a pull request Sep 15, 2020 that will close this issue
@ZeeshanAhmadKhalil
Copy link

I had same issue, reinstalling the react-native-screens resolved the issue.

@wcandillon
Copy link

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.

@WoLewicki
Copy link
Member

Only one Screen component of ScreenContainer can have activityState with value 2 at the same time and that Screen is responsive.

@wcandillon
Copy link

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?

@WoLewicki
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.