Skip to content

Commit 3585d9a

Browse files
feat: prevent native back button dismissal on iOS (#1773)
PR adding intercepting of native header back button on iOS, so the back animation does not fire there if it should have been intercepted. Co-authored-by: Kacper Kapuściak <39658211+kacperkapusciak@users.noreply.github.com>
1 parent c043a68 commit 3585d9a

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

ios/RNSScreen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ NS_ASSUME_NONNULL_BEGIN
111111
#endif
112112

113113
- (void)notifyTransitionProgress:(double)progress closing:(BOOL)closing goingForward:(BOOL)goingForward;
114+
- (void)notifyDismissCancelledWithDismissCount:(int)dismissCount;
114115
- (BOOL)isModal;
115116

116117
@end

ios/RNSScreenStack.mm

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,21 @@ - (void)initCommonProps
140140
[_controller setViewControllers:@[ [UIViewController new] ]];
141141
}
142142

143+
#pragma mark - helper methods
144+
145+
- (BOOL)shouldCancelDismissFromView:(RNSScreenView *)fromView toView:(RNSScreenView *)toView
146+
{
147+
int fromIndex = (int)[_reactSubviews indexOfObject:fromView];
148+
int toIndex = (int)[_reactSubviews indexOfObject:toView];
149+
for (int i = fromIndex; i > toIndex; i--) {
150+
if (_reactSubviews[i].preventNativeDismiss) {
151+
return YES;
152+
break;
153+
}
154+
}
155+
return NO;
156+
}
157+
143158
#pragma mark - Common
144159

145160
- (void)emitOnFinishTransitioningEvent
@@ -566,10 +581,14 @@ - (void)dismissOnReload
566581
} else if (operation == UINavigationControllerOperationPop) {
567582
screen = ((RNSScreen *)fromVC).screenView;
568583
}
584+
BOOL shouldCancelDismiss = [self shouldCancelDismissFromView:(RNSScreenView *)fromVC.view
585+
toView:(RNSScreenView *)toVC.view];
569586
if (screen != nil &&
570-
// we need to return the animator when full width swiping even if the animation is not custom,
587+
// when preventing the native dismiss with back button, we have to return the animator.
588+
// Also, we need to return the animator when full width swiping even if the animation is not custom,
571589
// otherwise the screen will be just popped immediately due to no animation
572-
(_isFullWidthSwiping || [RNSScreenStackAnimator isCustomAnimation:screen.stackAnimation])) {
590+
((operation == UINavigationControllerOperationPop && shouldCancelDismiss) || _isFullWidthSwiping ||
591+
[RNSScreenStackAnimator isCustomAnimation:screen.stackAnimation])) {
573592
return [[RNSScreenStackAnimator alloc] initWithOperation:operation];
574593
}
575594
return nil;
@@ -727,6 +746,26 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
727746
interactionControllerForAnimationController:
728747
(id<UIViewControllerAnimatedTransitioning>)animationController
729748
{
749+
RNSScreenView *fromView = [_controller.transitionCoordinator viewForKey:UITransitionContextFromViewKey];
750+
RNSScreenView *toView = [_controller.transitionCoordinator viewForKey:UITransitionContextToViewKey];
751+
// we can intercept clicking back button here, we check reactSuperview since this method also fires when
752+
// navigating back from JS
753+
if (_interactionController == nil && fromView.reactSuperview) {
754+
BOOL shouldCancelDismiss = [self shouldCancelDismissFromView:fromView toView:toView];
755+
if (shouldCancelDismiss) {
756+
_interactionController = [UIPercentDrivenInteractiveTransition new];
757+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
758+
[self->_interactionController cancelInteractiveTransition];
759+
self->_interactionController = nil;
760+
int fromIndex = (int)[self->_reactSubviews indexOfObject:fromView];
761+
int toIndex = (int)[self->_reactSubviews indexOfObject:toView];
762+
int indexDiff = fromIndex - toIndex;
763+
int dismissCount = indexDiff > 0 ? indexDiff : 1;
764+
[self updateContainer];
765+
[fromView notifyDismissCancelledWithDismissCount:dismissCount];
766+
});
767+
}
768+
}
730769
return _interactionController;
731770
}
732771

0 commit comments

Comments
 (0)