react-native-reanimated icon indicating copy to clipboard operation
react-native-reanimated copied to clipboard

[V3][iOS] Shared element transition repositioning with formSheet/modal presentation screens that have header

Open gorbypark opened this issue 1 year ago • 8 comments

Description

When using a shared element transition, there is some content repositioning in screens that have their presentation type set to formSheet or modal, and there is a header. With formSheet, the transitioned element goes to the top before "popping" back down into its correct place. With modal, it actually is a bit lower than the header and "pops" up, it's less noticeable on elements that are travelling in an upward motion, but it's there.

Here is a video of the issue with formSheet:

https://user-images.githubusercontent.com/19569469/222235337-d81dba96-9be3-4213-ae6d-a2d6657d2e72.mov

Here is a video of the issue with modal:

https://user-images.githubusercontent.com/19569469/222236093-1868eaae-8b88-4be1-96e5-49ddff4f2560.mov

As a control, here is a video of the exact same but with the header set to false:

https://user-images.githubusercontent.com/19569469/222235462-d604c6cf-e2bb-479b-af4a-287ecd122890.mov

There's not really an exact equivalent on Android, but here's a video of the exact same code on Android (presentation: modal):

https://user-images.githubusercontent.com/19569469/222240343-273a0069-2bb7-4f59-bb3e-11e5679f3592.mov

Steps to reproduce

  1. Add a shared transition to a screen that has it's presentation prop set to formSheet or modal

Snack or a link to a repository

https://github.com/gorbypark/reanimated-with-formSheet

Reanimated version

3.0.0

React Native version

0.71.3

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

gorbypark avatar Mar 01 '23 19:03 gorbypark

Hi @gorbypark, thanks for letting us know about the issue and providing a reproduction! I confirmed myself that the issue with Modals occurs on the "iPhone 14" simulator, but for some reason it doesn't on "iPhone 14 Pro" 🤔, I wonder where the difference in behaviour comes from. @piaskowyk

The issue becomes way more visible if you use a custom transition, like

const transition = SharedTransition.custom(values => {
  'worklet';
  return {
    width: withTiming(values.targetWidth, {duration: 5000}),
    height: withTiming(values.targetHeight, {duration: 5000}),
    originX: withTiming(values.targetOriginX, {duration: 5000}),
    originY: withTiming(values.targetOriginY, {duration: 5000}),
  };
});

iPhone 14:

https://user-images.githubusercontent.com/49338439/222680105-a8cb9584-3311-47b5-bdcd-b3ef998bf299.mp4

and 14 Pro:

https://user-images.githubusercontent.com/49338439/222680345-b7b993b7-0ebe-4de1-a31f-b857af53b245.mp4

jwajgelt avatar Mar 03 '23 09:03 jwajgelt

That said, the issue with presentation: 'formSheet' occurs on both.

jwajgelt avatar Mar 03 '23 09:03 jwajgelt

I was guessing it's react-navigation header height issue, but I can't seem to recreate anything using some quick console.log debugging. I also tried on a real device, iPhone 11 Pro, and it does the same as my original simulator videos. Something seems different between a notch and the dynamic island.

In screen2:

  const height = useHeaderHeight();

  useEffect(() => {
    console.log('height changed:', height);
  }, [height]);

  useEffect(() => {
    console.log('rendered, height is:', height);
  });

  useEffect(() => {
    console.log('mounted, height is:', height);
  }, []);

This always prints out 56 and doesn't seem to report back 0 at any point.

LOG  height changed: 56
LOG  rendered, height is: 56
LOG  mounted, height is: 56

gorbypark avatar Mar 03 '23 13:03 gorbypark

One more data point, when using a normal screen with no header, and wrapping the screen in a <SafeAreaView> (imported from react-native), the same behaviour is seen as the original issue (screen set to formSheet, with a header). Seems like a Safe Area issue.

function Screen2() {
  const navigation = useNavigation();
  const route = useRoute();
  const {width} = useWindowDimensions();

  return (
    <SafeAreaView style={{flex: 1}}>
      <View style={{flex: 1}}>
        <Box
          // React-navigation not typed as this is just a minimal repro
          // @ts-expect-error
          title={route.params?.id}
          width={width}
          height={300}
          bg="red"
          // React-navigation not typed as this is just a minimal repro
          // @ts-expect-error
          id={route.params?.id}
          align="flex-start"
          margin={false}
        />
        <Button title="back" onPress={() => navigation.goBack()} />
      </View>
    </SafeAreaView>
  );
}

https://user-images.githubusercontent.com/19569469/222838345-98244249-6f05-4a02-a953-1c294c4f3527.mov

gorbypark avatar Mar 03 '23 22:03 gorbypark

+1

hamdij0maa avatar Jun 21 '23 17:06 hamdij0maa

@gorbypark try adding animation: 'none' to the Screen options

virdesai avatar Jul 13 '23 20:07 virdesai

Hi guys, had the same problem on a brand-new app. The problem was that I was using à custom pressable animation to fire the router.push. I just replaced with the basic Pressable component, and it solves the issue for me

ipseidev avatar Jul 21 '23 17:07 ipseidev

any updates to this? also experiencing this repositioning issue with an <Animated.View> inside <SafeAreaView>. I've found that if I wrap the <Animated.View> inside an extra view i.e. <View style={{ flex: 1 }}> that does fix it, but that seems a bit weird?

alaughlin avatar Feb 13 '24 04:02 alaughlin