Skip to content

Commit

Permalink
fix(iOS): restore behaviour of RNSScreenStackAnimationNone (#2565)
Browse files Browse the repository at this point in the history
## Description

There is still some header animation noticeable for some reason. <--
This is because we use `fade` transition with duration 0 and do not
override interruptible animator! To prevent the animation we could
either return `nil` interruptible animator (but overriding the method
comes with it's own set of problems, see #2563 and other related) or
handle the `none` animation much earlier, when calling
`showViewControllers:animated:` in `updateContainer` (pass `animated:
NO`).

PS: If we would want to pass `animated: NO` I wonder what would happen
to dismiss prevention - we implemented it at the stage of the animation
start... We need to think this through.

Note: Must be implemented with old animation API, because
`UIViewPropertyAnimator` does not allow for 0 duration (it uses default
if the specified animation duration is below some undocumented
treshold).

This regression was introduced with #2477 

## Changes

We now use old API for `animation: none` & still rely on fade animation
to implement it. Note the points made above ☝🏻 - we should refactor this
code to make advantage of `animated:` parameter of the
`showViewControllers:animated:`.

## Test code and steps to reproduce

`TestAnimation` - set stack presentation to `none` - it works as prior
to v4.

WIP VIDEO

## Checklist

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Updated documentation: <!-- For adding new props to native-stack
-->
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [ ] Ensured that CI passes
  • Loading branch information
kkafar authored Dec 16, 2024
1 parent 6e37c7e commit 178d94d
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 26 deletions.
9 changes: 7 additions & 2 deletions apps/src/tests/TestAnimation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,18 @@ function Fifth({ navigation }: RoutePropBase<'Fifth'>): React.ReactNode {
);
}

function HeaderRight() {
return (
<Square size={20} color="blue" />
);
}

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
fullScreenGestureEnabled: true,
animation: 'simple_push',
//animationMatchesGesture: true,
headerRight: HeaderRight,
}}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Second" component={Second} options={{
Expand Down
2 changes: 2 additions & 0 deletions ios/RNSScreenStackAnimator.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma once

#import "RNSScreen.h"

@interface RNSScreenStackAnimator : NSObject <UIViewControllerAnimatedTransitioning>
Expand Down
85 changes: 61 additions & 24 deletions ios/RNSScreenStackAnimator.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)t
}

if (screen != nil && screen.stackAnimation == RNSScreenStackAnimationNone) {
return 0;
return 0.0;
}

if (screen != nil && screen.transitionDuration != nil && [screen.transitionDuration floatValue] >= 0) {
Expand All @@ -70,12 +70,6 @@ - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)t
return _transitionDuration;
}

//- (id<UIViewImplicitlyAnimating>)interruptibleAnimatorForTransition:
// (id<UIViewControllerContextTransitioning>)transitionContext
//{
// return _inFlightAnimator;
//}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
Expand Down Expand Up @@ -489,6 +483,38 @@ - (void)animateWithNoAnimation:(id<UIViewControllerContextTransitioning>)transit
}
}

- (void)animateNoneWithTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
toVC:(UIViewController *)toViewController
fromVC:(UIViewController *)fromViewController
{
toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];

if (_operation == UINavigationControllerOperationPush) {
[[transitionContext containerView] addSubview:toViewController.view];
toViewController.view.alpha = 0.0;
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
toViewController.view.alpha = 1.0;
}
completion:^(BOOL finished) {
toViewController.view.alpha = 1.0;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
} else if (_operation == UINavigationControllerOperationPop) {
[[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];

[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromViewController.view.alpha = 0.0;
}
completion:^(BOOL finished) {
fromViewController.view.alpha = 1.0;

[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
}

#pragma mark - Public API

- (nullable id<UITimingCurveProvider>)timingParamsForAnimationCompletion
Expand All @@ -509,24 +535,35 @@ - (void)animateTransitionWithStackAnimation:(RNSScreenStackAnimation)animation
toVC:(UIViewController *)toVC
fromVC:(UIViewController *)fromVC
{
if (animation == RNSScreenStackAnimationSimplePush) {
[self animateSimplePushWithShadowEnabled:shadowEnabled transitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
} else if (animation == RNSScreenStackAnimationSlideFromLeft) {
[self animateSlideFromLeftWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
} else if (animation == RNSScreenStackAnimationFade || animation == RNSScreenStackAnimationNone) {
[self animateFadeWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
} else if (animation == RNSScreenStackAnimationSlideFromBottom) {
[self animateSlideFromBottomWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
} else if (animation == RNSScreenStackAnimationFadeFromBottom) {
[self animateFadeFromBottomWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
switch (animation) {
case RNSScreenStackAnimationSimplePush:
[self animateSimplePushWithShadowEnabled:shadowEnabled
transitionContext:transitionContext
toVC:toVC
fromVC:fromVC];
return;
case RNSScreenStackAnimationSlideFromLeft:
[self animateSlideFromLeftWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
case RNSScreenStackAnimationFade:
[self animateFadeWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
case RNSScreenStackAnimationSlideFromBottom:
[self animateSlideFromBottomWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
case RNSScreenStackAnimationFadeFromBottom:
[self animateFadeFromBottomWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
case RNSScreenStackAnimationNone:
[self animateNoneWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
return;
default:
// simple_push is the default custom animation
[self animateSimplePushWithShadowEnabled:shadowEnabled
transitionContext:transitionContext
toVC:toVC
fromVC:fromVC];
}
// simple_push is the default custom animation
[self animateSimplePushWithShadowEnabled:shadowEnabled transitionContext:transitionContext toVC:toVC fromVC:fromVC];
}

+ (UISpringTimingParameters *)defaultSpringTimingParametersApprox
Expand Down

0 comments on commit 178d94d

Please sign in to comment.