facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.58k stars 24.37k forks source link

Animated.View doesn't respect zIndex #23836

Closed haddow777 closed 4 years ago

haddow777 commented 5 years ago

🐛 Bug Report

I am trying to follow a tutorial on building a simple stack swiping app similar to how apps like Tinder works. Basically you stack cards and you swipe the top most one to the left or right. Doing so shifts the deck up so the next card is the new top most card. In our tutorial, this is done by using position absolute on a number of View components and an Animated View component. The order in which they are rendered is so the Animated.View is rendered last. The problem is that, at least when I render it on my Android device, the Animated.View is always on the bottom. I have tried changing the zIndex of the Animated View, but it has no effect. No matter what I do, the Animated.View is always behind all the others.

To Reproduce

Generate an array of Views, the last of which is an Animated.View, and style them so they are all position: absolute. Render on an Android devices.

Expected Behavior

When rendered last, the Animated.View should be rendered on top of all the other Views. Also, when the zIndex is changed to a very high number, the Animated.View should be rendered on top of all other components.

Code Example

Full code can be found at https://github.com/haddow777/swipe

bascially, the code that generates the Views is this

renderCards() {
    if (this.state.index >= this.props.data.length) {
      return this.props.renderNoMoreCards();
    }
    return this.props.data
      .map((item, i) => {
        if (i < this.state.index) return null; //
        if (i === this.state.index) {
          return (
            <Animated.View
              key={item.id}
              {...this.state.panResponder.panHandlers}
              style={[
                this.getCardStyle(),
                styles.cardStyle,
                { zIndex: this.props.data.length + 1000 }
              ]}
            >
              {this.props.renderCard(item)}
            </Animated.View>
          );
        }

        return (
          <View key={item.id} style={styles.cardStyle}>
            {this.props.renderCard(item)}
          </View>
        );
      })
      .reverse();
  }

  render() {
    return <View>{this.renderCards()}</View>;
  }
}

const styles = {
  cardStyle: {
    position: "absolute",
    width: SCREEN_WIDTH
  }
};

Environment

 React Native Environment Info:
    System:
      OS: Windows 10
      CPU: (4) x64 Intel(R) Core(TM) i5-4300U CPU @ 1.90GHz
      Memory: 2.72 GB / 7.91 GB
    Binaries:
      Yarn: 1.13.0 - C:\Users\SURFACE\AppData\Roaming\npm\yarn.CMD
      npm: 6.8.0 - C:\Program Files\nodejs\npm.CMD
    IDEs:
      Android Studio: Version  3.2.0.0 AI-181.5540.7.32.5056338
 Expo CLI 2.11.6 environment info:
    System:
      OS: Windows 10
    Binaries:
      Yarn: 1.13.0 - C:\Users\SURFACE\AppData\Roaming\npm\yarn.CMD
      npm: 6.8.0 - C:\Program Files\nodejs\npm.CMD
    IDEs:
      Android Studio: Version  3.2.0.0 AI-181.5540.7.32.5056338

Android version 8.0.0 on Samsung Galaxy Note 8

haddow777 commented 5 years ago

Also, it should be noted, that in the tutorial, which is a video, when they added the reverse() to the array map that outputs all the components, the Animated.View was on top. In the view, they used an Iphone simulator though. Other searches I have done have shown other people having zIndex issues with Animated.View on Android while not on Iphones.

haddow777 commented 5 years ago

Additional note. I changed the code so each view being output is an Animated View. It did not change anything. Still the on that is being animated is under the rest. So it doesn't have anything to do with the type of View, it has more to do with the fact that one has the PanResponder attached to it.

JKCooper2 commented 5 years ago

zIndex on Android is known to have issues (or at least require different code to get the solution than ios) https://github.com/facebook/react-native/issues/8968 https://github.com/facebook/react-native/issues/18344 https://github.com/facebook/react-native/issues/698

Details on zIndex stacking context: https://philipwalton.com/articles/what-no-one-told-you-about-z-index/

stale[bot] commented 5 years ago

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

ksegla commented 5 years ago

elevation helped in my case. Component is rendered on top but there are still all kinds of weird things. When the Animated View is on top of something clickable, that something takes precedence. Also, anything that was ever below the Animated View can't be clicked anymore. A mess.

codypearce commented 5 years ago

This has come up as an issue on my library with the <AppbarBottom />component when I started wrapping components in an <Animated.View /> to scale them on load. It works fine on iOS and the Web as can be seen in the docs and storybook. However, on android you cannot click the fab .

The Fab component

The <Fab />'s <Animated.View /> is positioned absolute and has a zindex higher than the other components. Here's a simplified version

 <Animated.View
        style={[
          {
            transform: [{ scale: scale }],
            elevation: 100,
            zIndex: 100,
            position: 'absolute', 
          }
        ]}
>
        <Ripple
         style={[
           {
               position: 'absolute',
               zIndex: 100,
            },
          ]}
    >
          {children}
        </Ripple>
      </Animated.View>

The Problem

animatedview

1. Half Touchable If I remove the transform I can click on the <Fab /> but only the part that is contained within the <AppbarBottom />, not the overflowed part at the top.

bottomripple

2. Full Touchable If I refactor the <AppbarBottom /> to render the <Fab /> completely outside of the rest of components then the whole <Fab /> is touchable again:

success

Further testing:

  1. Adding the transform back again to the <Animated.View />, with the refactored structure above, again makes the <Fab /> untouchable.
  2. Removing the <Animated.View /> completely or replacing with a <View /> with the refactored structured also allows for full touchable again.
  3. Removing the <Animated.View /> completely or replacing with a <View />, but not using the refactored structure, so the <Fab /> is back inside the <AppbarBottom />, makes the <Fab /> only touchable on the bottom part again.
  4. Removing position: absolute on both the <Animated.View /> and the <Fab /> so that appear above the <AppbarBottom /> means that it is fully touchable. But then the <Fab /> does not match Material docs because it's above the <AppbarBottom />.

7. Success Removing absolute from the <Fab /> and keeping position: absolute on the <Animated.View /> with the transform and the refactored structure finally produced the result I wanted: success2

Takeways:

  1. Through all of this testing both iOS and the Web (using RNW) were able to fully touch the <Fab />, which mean the problem is specifically with Android.
  2. Adding position: absolute to a Touchable component of Animated.View causes problems with touchability.
  3. Something wrong with zindex or component ordering and touchability on Android since test 5 showed that a touchable component that is nested in a component and positioned absolute, cannot be touched outside of the wrapping component.

This is somewhat of a complicated example, but it is a real use case with examples where Android is not working correctly. If it would help I can recreate these tests with very stripped down components. Anyway I hope someone finds this useful in debugging their app.

FrickHazard commented 5 years ago

@codypearce Thanks for the in depth work. Same issues with our code base. All issues specific to android.

stale[bot] commented 4 years ago

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

stale[bot] commented 4 years ago

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.