diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java index d73de5dda3..c3e9bd18ae 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java @@ -91,7 +91,7 @@ public void setProperty(T view, String propName, @Nullable Object value) { mViewManager.setStackAnimation(view, (String) value); break; case "transitionDuration": - mViewManager.setTransitionDuration(view, value == null ? 350 : ((Double) value).intValue()); + mViewManager.setTransitionDuration(view, value == null ? 500 : ((Double) value).intValue()); break; case "replaceAnimation": mViewManager.setReplaceAnimation(view, (String) value); diff --git a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md index d4635f0283..b8c812ad72 100644 --- a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md +++ b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md @@ -297,7 +297,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 `350`. +Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade`, `slide_from_left` and `simple_push` transitions on iOS. Defaults to `500`. The duration of `default` and `flip` transitions isn't customizable. diff --git a/ios/RNSScreenStackAnimator.mm b/ios/RNSScreenStackAnimator.mm index abb2cf69fd..20741b60a2 100644 --- a/ios/RNSScreenStackAnimator.mm +++ b/ios/RNSScreenStackAnimator.mm @@ -5,13 +5,17 @@ // proportions to default transition duration static const float RNSSlideOpenTransitionDurationProportion = 1; -static const float RNSFadeOpenTransitionDurationProportion = 0.2 / 0.35; -static const float RNSSlideCloseTransitionDurationProportion = 0.25 / 0.35; -static const float RNSFadeCloseTransitionDurationProportion = 0.15 / 0.35; -static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35; +static const float RNSFadeOpenTransitionDurationProportion = 0.2 / 0.5; +static const float RNSSlideCloseTransitionDurationProportion = 0.25 / 0.5; +static const float RNSFadeCloseTransitionDurationProportion = 0.15 / 0.5; +static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.5; // same value is used in other projects using similar approach for transistions // and it looks the most similar to the value used by Apple static constexpr float RNSShadowViewMaxAlpha = 0.1; +static const int UIViewAnimationOptionCurveDefaultTransition = 7 << 16; +// static constexpr UIViewAnimationOptions RNSUIViewAnimationOptionsCommon = UIViewAnimationOptionCurveEaseInOut; +static constexpr UIViewAnimationOptions RNSUIViewAnimationOptionsCommon = 7 << 16; +// static constexpr UIViewAnimationOptions RNSUIViewAnimationOptionsCommon = UIViewAnimationOptionCurveLinear; @implementation RNSScreenStackAnimator { UINavigationControllerOperation _operation; @@ -22,7 +26,7 @@ - (instancetype)initWithOperation:(UINavigationControllerOperation)operation { if (self = [super init]) { _operation = operation; - _transitionDuration = 0.35; // default duration in seconds + _transitionDuration = 0.5; // default duration in seconds } return self; } @@ -128,22 +132,33 @@ - (void)animateSimplePushWithShadowEnabled:(BOOL)shadowEnabled shadowView.alpha = 0.0; } - [UIView animateWithDuration:[self transitionDuration:transitionContext] - animations:^{ - fromViewController.view.transform = leftTransform; - toViewController.view.transform = CGAffineTransformIdentity; - if (shadowView) { - shadowView.alpha = RNSShadowViewMaxAlpha; - } - } - completion:^(BOOL finished) { - if (shadowView) { - [shadowView removeFromSuperview]; - } - fromViewController.view.transform = CGAffineTransformIdentity; - toViewController.view.transform = CGAffineTransformIdentity; - [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; - }]; + // Default curve provider is as defined below, however defined this way it ignores the `animationDuration` prop. + // Damping of 1.0 seems close enough. + // id timingCurveProvider = [[UISpringTimingParameters alloc] init]; + + id timingCurveProvider = [[UISpringTimingParameters alloc] initWithDampingRatio:1.0]; + + UIViewPropertyAnimator *animator = + [[UIViewPropertyAnimator alloc] initWithDuration:[self transitionDuration:transitionContext] + timingParameters:timingCurveProvider]; + + [animator addAnimations:^{ + fromViewController.view.transform = leftTransform; + toViewController.view.transform = CGAffineTransformIdentity; + if (shadowView) { + shadowView.alpha = RNSShadowViewMaxAlpha; + } + }]; + + [animator addCompletion:^(UIViewAnimatingPosition finalPosition) { + if (shadowView) { + [shadowView removeFromSuperview]; + } + fromViewController.view.transform = CGAffineTransformIdentity; + toViewController.view.transform = CGAffineTransformIdentity; + [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; + }]; + [animator startAnimation]; } else if (_operation == UINavigationControllerOperationPop) { toViewController.view.transform = leftTransform; [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view]; @@ -159,7 +174,7 @@ - (void)animateSimplePushWithShadowEnabled:(BOOL)shadowEnabled shadowView.alpha = 0.0; } }; - void (^completionBlock)(BOOL) = ^(BOOL finished) { + void (^completionBlock)(UIViewAnimatingPosition) = ^(UIViewAnimatingPosition finalPosition) { if (shadowView) { [shadowView removeFromSuperview]; } @@ -169,16 +184,24 @@ - (void)animateSimplePushWithShadowEnabled:(BOOL)shadowEnabled }; if (!transitionContext.isInteractive) { - [UIView animateWithDuration:[self transitionDuration:transitionContext] - animations:animationBlock - completion:completionBlock]; + id timingCurveProvider = [[UISpringTimingParameters alloc] initWithDampingRatio:1.0]; + + UIViewPropertyAnimator *animator = + [[UIViewPropertyAnimator alloc] initWithDuration:[self transitionDuration:transitionContext] + timingParameters:timingCurveProvider]; + + [animator addAnimations:animationBlock]; + [animator addCompletion:completionBlock]; + [animator startAnimation]; } else { // we don't want the EaseInOut option when swiping to dismiss the view, it is the same in default animation option - [UIView animateWithDuration:[self transitionDuration:transitionContext] - delay:0.0 - options:UIViewAnimationOptionCurveLinear - animations:animationBlock - completion:completionBlock]; + UIViewPropertyAnimator *animator = + [[UIViewPropertyAnimator alloc] initWithDuration:[self transitionDuration:transitionContext] + curve:UIViewAnimationCurveLinear + animations:animationBlock]; + + [animator addCompletion:completionBlock]; + [animator startAnimation]; } } } @@ -203,6 +226,8 @@ - (void)animateSlideFromLeftWithTransitionContext:(id; stackAnimation?: WithDefault; - transitionDuration?: WithDefault; + transitionDuration?: WithDefault; replaceAnimation?: WithDefault; swipeDirection?: WithDefault; hideKeyboardOnSwipe?: boolean; diff --git a/src/fabric/ScreenNativeComponent.ts b/src/fabric/ScreenNativeComponent.ts index 1eb9071ac3..bc0631e556 100644 --- a/src/fabric/ScreenNativeComponent.ts +++ b/src/fabric/ScreenNativeComponent.ts @@ -99,7 +99,7 @@ export interface NativeProps extends ViewProps { gestureResponseDistance?: GestureResponseDistanceType; stackPresentation?: WithDefault; stackAnimation?: WithDefault; - transitionDuration?: WithDefault; + transitionDuration?: WithDefault; replaceAnimation?: WithDefault; swipeDirection?: WithDefault; hideKeyboardOnSwipe?: boolean;