Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader

class MainApplication : Application(), ReactApplication {

Expand Down
50 changes: 31 additions & 19 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -398,27 +398,21 @@ class Screen(
return
}
isBeingRemoved = false
endTransitionRecursive(this)
endViewTransition()
}

private fun endTransitionRecursive(parent: ViewGroup) {
parent.children.forEach { childView ->
parent.endViewTransition(childView)

if (childView is ScreenStackHeaderConfig) {
endTransitionRecursive(childView.toolbar)
}

if (childView is ViewGroup) {
endTransitionRecursive(childView)
}
}
}
private val transitioningViews = mutableListOf<Pair<ViewGroup, View>>()

/**
* Called when a screen gets removed. This is to mark all children as in transition,
* so they are not removed from the view hierarchy until endRemovalTransition is called.
* This is needed for the screen transition animation to work properly (otherwise the children
* would instantly disappear from the screen).
*/
private fun startTransitionRecursive(parent: ViewGroup?) {
parent?.let {
for (i in 0 until it.childCount) {
val child = it.getChildAt(i)
parent?.let { parentView ->
for (i in 0 until parentView.childCount) {
val child = parentView.getChildAt(i)

if (parent is SwipeRefreshLayout && child is ImageView) {
// SwipeRefreshLayout class which has CircleImageView as a child,
Expand All @@ -427,9 +421,12 @@ class Screen(
// wrong index if we called `startViewTransition` on the views on new arch.
// We add a simple View to bump the number of children to make it work.
// TODO: find a better way to handle this scenario
it.addView(View(context), i)
parentView.addView(View(context), i)
} else {
child?.let { view -> it.startViewTransition(view) }
child?.let { childView ->
parentView.startViewTransition(childView)
transitioningViews.add(Pair(parentView, childView))
}
}

if (child is ScreenStackHeaderConfig) {
Expand All @@ -445,6 +442,21 @@ class Screen(
}
}

/**
* Called when the removal transition is finished. This will clear the transition state
* from all children and allow them to be removed from the view hierarchy and their mParent
* field to be set to null.
*/
private fun endViewTransition() {
// IMPORTANT: Reverse order is needed, inner children first!
// Otherwise parents will call dispatchOnDetachedFromWindow on all their children,
// which will cause endViewTransition to have no effect on them anymore.
transitioningViews.asReversed().forEach { (parent, child) ->
parent.endViewTransition(child)
}
transitioningViews.clear()
}

// We do not want to perform any action, therefore do not need to override the associated method.
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean =
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class ScreenStack(
}

override fun endViewTransition(view: View) {
if (view is ScreensCoordinatorLayout) {
view.fragment.screen.endRemovalTransition()
}

super.endViewTransition(view)

disappearingTransitioningChildren.remove(view)
Expand Down
66 changes: 66 additions & 0 deletions apps/src/tests/TestRecyclingViews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import {Button, requireNativeComponent, View} from 'react-native';
import {NavigationContainer, useNavigation} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

let CustomView = requireNativeComponent('CustomView');

function HomeScreen() {
const navigation = useNavigation();
return (
<View
collapsable={false}
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
<CustomView
style={{
width: 100,
height: 100,
}}
/>
</View>
);
}

function DetailsScreen() {
const navigation = useNavigation();
return (
<View
collapsable={false}
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Button title="Go to Home" onPress={() => navigation.goBack()} />
<CustomView
style={{
width: 100,
height: 100,
}}
/>
</View>
);
}

export default function TestApp() {
return (
<View
style={{
flex: 1,
}}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,4 @@ export { default as TestBottomTabsOrientation } from './TestBottomTabsOrientatio
export { default as TestScreenStack } from './TestScreenStack';
export { default as TestSplitView } from './TestSplitView';
export { default as TestSafeAreaViewIOS } from './TestSafeAreaViewIOS';
export { default as TestRecyclingViews } from './TestRecyclingViews';
6 changes: 3 additions & 3 deletions src/gesture-handler/fabricUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import { View } from "react-native";
import { View } from 'react-native';

/* eslint-disable */

Expand All @@ -24,11 +24,11 @@ export function getShadowNodeWrapperAndTagFromRef(ref: View | null): {
return {
shadowNodeWrapper: null,
tag: -1,
}
};
}
const internalRef = ref as unknown as HostInstance;
return {
shadowNodeWrapper: internalRef.__internalInstanceHandle.stateNode.node,
tag: internalRef.__nativeTag,
}
};
}