Skip to content

Commit

Permalink
[Layout Animations][iOS] Fix registering exiting view ancestors (soft…
Browse files Browse the repository at this point in the history
…ware-mansion#3849)

<!-- Thanks for submitting a pull request! We appreciate you spending
the time to work on these changes. Please follow the template so that
the reviewers can easily understand what the code changes affect. -->

## Summary

<!-- Explain the motivation for this PR. Include "Fixes #<number>" if
applicable. -->
software-mansion#3824 introduces a bug with how we register ancestors of views with
`exiting` animations.
When a tree with `exiting` views is reattached to the native view
hierarchy, we don't register the ancestors of that tree as ancestors of
exiting subviews. This may cause issues with deleting these views early,
while there're still `exiting` animations running inside them.

## Test plan

<!-- Provide a minimal but complete code snippet that can be used to
test out this change along with instructions how to run it and a
description of the expected behavior. -->
  • Loading branch information
jwajgelt authored and fluiddot committed Jun 5, 2023
1 parent ff9d76c commit a40d12c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 1 deletion.
5 changes: 5 additions & 0 deletions Example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
BasicNestedLayoutAnimation,
BasicNestedAnimation,
BasicLayoutAnimation,
DeleteAncestorOfExiting,
} from './LayoutReanimation';

import AnimatedStyleUpdateExample from './AnimatedStyleUpdateExample';
Expand Down Expand Up @@ -69,6 +70,10 @@ if (Platform.OS === 'android') {
type Screens = Record<string, { screen: React.ComponentType; title?: string }>;

const SCREENS: Screens = {
DeleteAncestorOfExiting: {
screen: DeleteAncestorOfExiting,
title: '🆕 Deleting view with an exiting animation',
},
BasicLayoutAnimation: {
screen: BasicLayoutAnimation,
title: '🆕 Basic layout animation',
Expand Down
54 changes: 54 additions & 0 deletions Example/src/LayoutReanimation/DeleteAncestorOfExiting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Animated, { PinwheelOut } from 'react-native-reanimated';
import { Button, StyleSheet, View } from 'react-native';

import React from 'react';

export function DeleteAncestorOfExiting() {
const [outer, setOuter] = React.useState(false);
const [inner, setInner] = React.useState(true);

return (
<View style={styles.container}>
<Button
onPress={() => {
setOuter(!outer);
setInner(!outer);
}}
title="Toggle Outer"
/>
<Button onPress={() => setInner(!inner)} title="Toggle Inner" />
{outer && (
<View style={styles.outerBox}>
<Animated.View style={styles.box} exiting={PinwheelOut} />
{inner && (
<Animated.View
style={styles.box}
exiting={PinwheelOut.duration(5000)}
/>
)}
</View>
)}
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
marginTop: 300,
},
outerBox: {
width: 260,
height: 150,
flexDirection: 'row',
backgroundColor: 'navy',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
marginLeft: 20,
backgroundColor: 'red',
},
});
1 change: 1 addition & 0 deletions Example/src/LayoutReanimation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './Nested';
export * from './BasicLayoutAnimation';
export * from './BasicNestedAnimation';
export * from './BasicNestedLayoutAnimation';
export * from './DeleteAncestorOfExiting';
13 changes: 12 additions & 1 deletion ios/LayoutReanimation/REAAnimationsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,17 @@ - (BOOL)wantsHandleRemovalOfView:(UIView *)view
}

- (void)registerExitingAncestors:(UIView *)child
{
[self registerExitingAncestors:child exitingSubviewsCount:1];
}

- (void)registerExitingAncestors:(UIView *)child exitingSubviewsCount:(int)exitingSubviewsCount
{
UIView *parent = child.superview;
while (parent != nil && ![parent isKindOfClass:[RCTRootView class]]) {
if (parent.reactTag != nil) {
_exitingSubviewsCountMap[parent.reactTag] = @([_exitingSubviewsCountMap[parent.reactTag] intValue] + 1);
_exitingSubviewsCountMap[parent.reactTag] =
@([_exitingSubviewsCountMap[parent.reactTag] intValue] + exitingSubviewsCount);
}
parent = parent.superview;
}
Expand Down Expand Up @@ -363,6 +369,11 @@ - (void)reattachAnimatedChildren:(NSArray<id<RCTComponent>> *)children
NSNumber *originalIndex = indices[i];
if ([self startAnimationsRecursive:childView shouldRemoveSubviewsWithoutAnimations:YES]) {
[(UIView *)container insertSubview:childView atIndex:[originalIndex intValue] - skippedViewsCount];
int exitingSubviewsCount = [_exitingSubviewsCountMap[childView.reactTag] intValue];
if ([_exitingViews objectForKey:childView.reactTag] != nil) {
exitingSubviewsCount++;
}
[self registerExitingAncestors:childView exitingSubviewsCount:exitingSubviewsCount];
} else {
skippedViewsCount++;
}
Expand Down

0 comments on commit a40d12c

Please sign in to comment.