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
2 changes: 1 addition & 1 deletion guides/GUIDE_FOR_LIBRARY_AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ Allows for the customization of how the given screen should appear/disappear whe
- `"simple_push"` – performs a default animation, but without shadow and native header transition (iOS only)
- `"slide_from_bottom"` - slide in the new screen from bottom to top
- `"slide_from_right"` - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
- `"slide_from_left"` - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
- `"slide_from_left"` - slide in the new screen from left to right
- `"ios"` - iOS like slide in animation (Android only, resolves to default transition on iOS)
- `"none"` – the screen appears/disappears without an animation

Expand Down
5 changes: 3 additions & 2 deletions ios/RNSConvert.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ + (RNSScreenStackPresentation)RNSScreenStackPresentationFromCppEquivalent:
+ (RNSScreenStackAnimation)RNSScreenStackAnimationFromCppEquivalent:(react::RNSScreenStackAnimation)stackAnimation
{
switch (stackAnimation) {
// these four are intentionally grouped
// these three are intentionally grouped
case react::RNSScreenStackAnimation::Slide_from_right:
case react::RNSScreenStackAnimation::Slide_from_left:
case react::RNSScreenStackAnimation::Ios:
case react::RNSScreenStackAnimation::Default:
return RNSScreenStackAnimationDefault;
case react::RNSScreenStackAnimation::Slide_from_left:
return RNSScreenStackAnimationSlideFromLeft;
case react::RNSScreenStackAnimation::Flip:
return RNSScreenStackAnimationFlip;
case react::RNSScreenStackAnimation::Simple_push:
Expand Down
1 change: 1 addition & 0 deletions ios/RNSEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef NS_ENUM(NSInteger, RNSScreenStackAnimation) {
RNSScreenStackAnimationFlip,
RNSScreenStackAnimationSlideFromBottom,
RNSScreenStackAnimationSimplePush,
RNSScreenStackAnimationSlideFromLeft,
};

typedef NS_ENUM(NSInteger, RNSScreenReplaceAnimation) {
Expand Down
3 changes: 2 additions & 1 deletion ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ - (void)setStackAnimation:(RNSScreenStackAnimation)stackAnimation
case RNSScreenStackAnimationSimplePush:
case RNSScreenStackAnimationSlideFromBottom:
case RNSScreenStackAnimationFadeFromBottom:
case RNSScreenStackAnimationSlideFromLeft:
// Default
break;
}
Expand Down Expand Up @@ -1572,7 +1573,7 @@ @implementation RCTConvert (RNSScreen)
@"simple_push" : @(RNSScreenStackAnimationSimplePush),
@"slide_from_bottom" : @(RNSScreenStackAnimationSlideFromBottom),
@"slide_from_right" : @(RNSScreenStackAnimationDefault),
@"slide_from_left" : @(RNSScreenStackAnimationDefault),
@"slide_from_left" : @(RNSScreenStackAnimationSlideFromLeft),
@"ios" : @(RNSScreenStackAnimationDefault),
}),
RNSScreenStackAnimationDefault,
Expand Down
18 changes: 13 additions & 5 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -749,12 +749,14 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
// Now we're dealing with RNSScreenEdgeGestureRecognizer (or _UIParallaxTransitionPanGestureRecognizer)
if (topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]) {
if ([gestureRecognizer isKindOfClass:[RNSScreenEdgeGestureRecognizer class]]) {
UIRectEdge edges = ((RNSScreenEdgeGestureRecognizer *)gestureRecognizer).edges;
BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
BOOL isSlideFromLeft = topScreen.stackAnimation == RNSScreenStackAnimationSlideFromLeft;
// if we do not set any explicit `semanticContentAttribute`, it is `UISemanticContentAttributeUnspecified` instead
// of `UISemanticContentAttributeForceLeftToRight`, so we just check if it is RTL or not
BOOL isCorrectEdge = (_controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft &&
((RNSScreenEdgeGestureRecognizer *)gestureRecognizer).edges == UIRectEdgeRight) ||
(_controller.view.semanticContentAttribute != UISemanticContentAttributeForceRightToLeft &&
((RNSScreenEdgeGestureRecognizer *)gestureRecognizer).edges == UIRectEdgeLeft);
BOOL isCorrectEdge = (isRTL && edges == UIRectEdgeRight) ||
(!isRTL && isSlideFromLeft && edges == UIRectEdgeRight) ||
(isRTL && isSlideFromLeft && edges == UIRectEdgeLeft) || (!isRTL && edges == UIRectEdgeLeft);
if (isCorrectEdge) {
[self cancelTouchesInParent];
return YES;
Expand Down Expand Up @@ -818,7 +820,10 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
}
}

bool isInverted = topScreen.stackAnimation == RNSScreenStackAnimationSlideFromLeft;

float transitionProgress = (translation / distance);
transitionProgress = isInverted ? transitionProgress * -1 : transitionProgress;

switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan: {
Expand All @@ -840,7 +845,10 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
case UIGestureRecognizerStateEnded: {
// values taken from
// https://github.com/react-navigation/react-navigation/blob/54739828598d7072c1bf7b369659e3682db3edc5/packages/stack/src/views/Stack/Card.tsx#L316
BOOL shouldFinishTransition = (translation + velocity * 0.3) > (distance / 2);
float snapPoint = distance / 2;
float gestureDistance = translation + velocity * 0.3;
gestureDistance = isInverted ? gestureDistance * -1 : gestureDistance;
BOOL shouldFinishTransition = gestureDistance > snapPoint;
if (shouldFinishTransition) {
[_interactionController finishInteractiveTransition];
} else {
Expand Down
61 changes: 61 additions & 0 deletions ios/RNSScreenStackAnimator.mm
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,64 @@ - (void)animateSimplePushWithTransitionContext:(id<UIViewControllerContextTransi
}
}

- (void)animateSlideFromLeftWithTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
toVC:(UIViewController *)toViewController
fromVC:(UIViewController *)fromViewController
{
float containerWidth = transitionContext.containerView.bounds.size.width;
float belowViewWidth = containerWidth * 0.3;

CGAffineTransform rightTransform = CGAffineTransformMakeTranslation(-containerWidth, 0);
CGAffineTransform leftTransform = CGAffineTransformMakeTranslation(belowViewWidth, 0);

if (toViewController.navigationController.view.semanticContentAttribute ==
UISemanticContentAttributeForceRightToLeft) {
rightTransform = CGAffineTransformMakeTranslation(containerWidth, 0);
leftTransform = CGAffineTransformMakeTranslation(-belowViewWidth, 0);
}

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

void (^animationBlock)(void) = ^{
toViewController.view.transform = CGAffineTransformIdentity;
fromViewController.view.transform = rightTransform;
};
void (^completionBlock)(BOOL) = ^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
toViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
};

if (!transitionContext.isInteractive) {
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:animationBlock
completion:completionBlock];
} 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];
}
}
}

- (void)animateFadeWithTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
toVC:(UIViewController *)toViewController
fromVC:(UIViewController *)fromViewController
Expand Down Expand Up @@ -330,6 +388,9 @@ - (void)animateTransitionWithStackAnimation:(RNSScreenStackAnimation)animation
if (animation == RNSScreenStackAnimationSimplePush) {
[self animateSimplePushWithTransitionContext: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;
Expand Down
2 changes: 1 addition & 1 deletion native-stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ How the given screen should appear/disappear when pushed or popped at the top of
- `simple_push` – performs a default animation, but without shadow and native header transition (iOS only)
- `slide_from_bottom` – performs a slide from bottom animation
- `slide_from_right` - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
- `slide_from_left` - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
- `slide_from_left` - slide in the new screen from left to right
- `ios` - iOS like slide in animation (Android only, resolves to default transition on iOS)
- `none` - the screen appears/disappears without an animation.

Expand Down
2 changes: 1 addition & 1 deletion src/native-stack/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export type NativeStackNavigationOptions = {
* - "simple_push" – performs a default animation, but without shadow and native header transition (iOS only)
* - "slide_from_bottom" – performs a slide from bottom animation
* - "slide_from_right" - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right
* - "ios" - iOS like slide in animation (Android only, resolves to default transition on iOS)
* - "none" – the screen appears/dissapears without an animation
*/
Expand Down
2 changes: 1 addition & 1 deletion src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export interface ScreenProps extends ViewProps {
* - "simple_push" – performs a default animation, but without shadow and native header transition (iOS only)
* - `slide_from_bottom` – performs a slide from bottom animation
* - "slide_from_right" - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right
* - "ios" - iOS like slide in animation (Android only, resolves to default transition on iOS)
* - "none" – the screen appears/dissapears without an animation
*/
Expand Down