Skip to content
Merged
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
92 changes: 92 additions & 0 deletions apps/src/tests/Test3074.tsx
Comment thread
kmichalikk marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useEffect, useRef } from 'react';
import { NavigationContainer, ParamListBase } from '@react-navigation/native';
import {
NativeStackNavigationProp,
createNativeStackNavigator,
} from '@react-navigation/native-stack';
import { Button, Text, View, Animated, useAnimatedValue } from 'react-native';
import Colors from '../shared/styling/Colors';
import { useTransitionProgress } from 'react-native-screens';

type StackRouteParamList = {
Screen1: undefined;
Screen2: undefined;
TModal1: undefined;
TModal2: undefined;
};

type NavigationProp<ParamList extends ParamListBase> = {
navigation: NativeStackNavigationProp<ParamList>;
};

type StackNavigationProp = NavigationProp<StackRouteParamList>;

const Stack = createNativeStackNavigator<StackRouteParamList>();

function makeScreen(route: keyof StackRouteParamList, navigateTo: keyof StackRouteParamList | undefined, color: string, uiOffset: number) {
return function Screen({ navigation }: StackNavigationProp) {
const { progress: animatedProgress, closing: animatedClosing, goingForward: animatedGoingForward } = useTransitionProgress();

const progress = animatedProgress.interpolate({ inputRange: [0, 1], outputRange: [1, 200] });
const closing = animatedClosing.interpolate({ inputRange: [0, 1], outputRange: [1, 200] });
const goingForward = animatedGoingForward.interpolate({ inputRange: [0, 1], outputRange: [1, 200] });

animatedProgress.addListener(v => console.log(`progress ${route} = ${Math.round(v.value * 100) / 100}`));
animatedClosing.addListener(v => console.log(`closing ${route} = ${v.value}`));
animatedGoingForward.addListener(v => console.log(`goingForward ${route} = ${v.value}`));

const spinValue = useAnimatedValue(0);
useEffect(() => {
Animated.loop(Animated.timing(spinValue, { toValue: 1, duration: 1000, useNativeDriver: true })).start();
}, []);
const spin = spinValue.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] });

return (
<>
<View style={{ position: 'absolute', marginTop: uiOffset ? uiOffset : 4, width: '100%', height: 100, backgroundColor: color }}>
{navigateTo != null && <Button title={"Go to " + navigateTo} onPress={() => navigation.navigate(navigateTo)} />}
<Button title="Go Back" onPress={() => navigation.goBack()} />
</View>
<View style={{ position: 'absolute', marginTop: uiOffset ? uiOffset : 4 }}>
<Text>Progress: </Text><Animated.View style={{ height: 10, width: 1, backgroundColor: Colors.RedDark100, transform: [{ scaleX: progress }] }} />
<Text>Closing: </Text><Animated.View style={{ height: 10, width: 1, backgroundColor: Colors.GreenDark100, transform: [{ scaleX: closing }] }} />
<Text>Going Forward: </Text><Animated.View style={{ height: 10, width: 1, backgroundColor: Colors.NavyDark100, transform: [{ scaleX: goingForward }] }} />
</View>
<View style={{ position: 'absolute', marginTop: (uiOffset ? uiOffset : 4) + 40, marginLeft: 300 }}>
<Animated.View style={{ position: 'absolute', backgroundColor: Colors.YellowDark100, transform: [{ rotate: spin }] }}><Text>freeze{"\n"}check</Text></Animated.View>
</View>
</>
)
}
}

export default function App() {
return (
<>
<NavigationContainer>
<Stack.Navigator screenOptions={{
autoHideHomeIndicator: true,
}}>
<Stack.Group>
<Stack.Screen name="Screen1" component={makeScreen("Screen1", "Screen2", Colors.BlueLight40, 0)} options={{ animation: "fade" }} />
<Stack.Screen name="Screen2" component={makeScreen("Screen2", "TModal1", Colors.BlueLight60, 80)} options={{ animation: "fade", animationDuration: 3000 }} />
</Stack.Group>
<Stack.Group screenOptions={{
presentation: "transparentModal",
}}>
<Stack.Screen name="TModal1" component={makeScreen("TModal1", "TModal2", Colors.BlueLight80, 160)} options={{ animation: "fade", animationDuration: 3000 }} />
<Stack.Screen name="TModal2" component={makeScreen("TModal2", undefined, Colors.BlueLight100, 240)} options={{ animation: "none" }} />
</Stack.Group>
</Stack.Navigator>
</NavigationContainer>
<Text style={{ position: 'absolute', marginTop: 450 }}>
The stack above consists of 2 screens and 2 transparent fullscreen modals.
When all of them are pushed on stack, Screen1 covers Screen2, and Screen2 is visible below both modals.
Notice the difference in transitions when clicking "Go Back", using header back button, and swiping.
{"\n\n"}
When using "Go back", screens will not be responsive, because React removes it before host is finished. Use back button to see the progress. Modals can only be dismissed using "Go Back",
so use logs to observe the changes.
</Text>
Comment thread
kmichalikk marked this conversation as resolved.
</>
);
}
1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export { default as Test2949 } from './Test2949'; // [E2E skipped]: can't check
export { default as Test2963 } from './Test2963'; // [E2E created](iOS): issue related to iOS
export { default as Test3004 } from './Test3004';
export { default as Test3045 } from './Test3045';
export { default as Test3074 } from './Test3074';
export { default as Test3093 } from './Test3093';
export { default as Test3111 } from './Test3111';
export { default as Test3115 } from './Test3115';
Expand Down
3 changes: 1 addition & 2 deletions guides/GUIDE_FOR_LIBRARY_AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,7 @@ When using `vertical` option, options `fullScreenSwipeEnabled: true`, `customAni
### `transitionDuration` (iOS only)

Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `500`.

The duration of `default` and `flip` transitions isn't customizable.
For screens with `default` and `flip` transitions, and, as of now, for screens with `presentation` set to `modal`, `formSheet`, `pageSheet` (regardless of transition), the duration isn't customizable.

### `useTransitionProgress`

Expand Down
33 changes: 22 additions & 11 deletions ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1715,19 +1715,30 @@ - (id)findFirstResponder:(UIView *)parent
- (void)setupProgressNotification
{
if (self.transitionCoordinator != nil) {
if (!self.transitionCoordinator.isAnimated) {
// If the transition is not animated, there is no point to set up animation
// and completion callbacks. This helps prevent issues with dismissed modals having
// "artifical animation duration" instead of being removed instantly.
// See: https://github.com/software-mansion/react-native-screens/pull/3189/
return;
}

_fakeView.alpha = 0.0;

auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[[context containerView] addSubview:self->_fakeView];
self->_fakeView.alpha = 1.0;
self->_animationTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleAnimation)];
[self->_animationTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
};

[self.transitionCoordinator
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[[context containerView] addSubview:self->_fakeView];
self->_fakeView.alpha = 1.0;
self->_animationTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleAnimation)];
[self->_animationTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self->_animationTimer setPaused:YES];
[self->_animationTimer invalidate];
[self->_fakeView removeFromSuperview];
}];
animateAlongsideTransition:animation
completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self->_animationTimer setPaused:YES];
[self->_animationTimer invalidate];
[self->_fakeView removeFromSuperview];
}];
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ export interface ScreenProps extends ViewProps {
swipeDirection?: SwipeDirectionTypes;
/**
* Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `500`.
* The duration of `default` and `flip` transitions isn't customizable.
* For screens with `default` and `flip` transitions, and, as of now, for screens with `presentation` set to `modal`, `formSheet`, `pageSheet` (regardless of transition), the duration isn't customizable.
Comment thread
kmichalikk marked this conversation as resolved.
*
* @platform ios
*/
Expand Down
Loading