From 990e6ef44201f72eab89fda069c01a76c541cea1 Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Wed, 12 Jul 2023 12:58:14 +0200 Subject: [PATCH] chore: add onGestureCancel event (#1810) ## Description This PR adds new iOS only event. Event is triggered after canceled swipe back (as on video) https://github.com/software-mansion/react-native-screens/assets/36106620/e9f63c7c-e6b4-4444-895a-d6b41c725963 --- ios/RNSScreen.h | 1 + ios/RNSScreen.mm | 17 +++++++++++++++++ src/fabric/ScreenNativeComponent.ts | 1 + src/index.native.tsx | 7 +++++++ src/native-stack/types.tsx | 4 ++++ src/native-stack/views/NativeStackView.tsx | 6 ++++++ src/types.tsx | 4 ++++ 7 files changed, 40 insertions(+) diff --git a/ios/RNSScreen.h b/ios/RNSScreen.h index c0b00abf9a..df10c44eef 100644 --- a/ios/RNSScreen.h +++ b/ios/RNSScreen.h @@ -97,6 +97,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) RCTDirectEventBlock onWillDisappear; @property (nonatomic, copy) RCTDirectEventBlock onNativeDismissCancelled; @property (nonatomic, copy) RCTDirectEventBlock onTransitionProgress; +@property (nonatomic, copy) RCTDirectEventBlock onGestureCancel; #endif // RCT_NEW_ARCH_ENABLED - (void)notifyFinishTransitioning; diff --git a/ios/RNSScreen.mm b/ios/RNSScreen.mm index d6975f7850..86ad9350fb 100644 --- a/ios/RNSScreen.mm +++ b/ios/RNSScreen.mm @@ -392,6 +392,20 @@ - (void)notifyDisappear #endif } +- (void)notifyGestureCancel +{ +#ifdef RCT_NEW_ARCH_ENABLED + if (_eventEmitter != nullptr) { + std::dynamic_pointer_cast(_eventEmitter) + ->onGestureCancel(facebook::react::RNSScreenEventEmitter::OnGestureCancel{}); + } +#else + if (self.onGestureCancel) { + self.onGestureCancel(nil); + } +#endif +} + - (BOOL)isMountedUnderScreenOrReactRoot { #ifdef RCT_NEW_ARCH_ENABLED @@ -886,6 +900,8 @@ - (void)viewDidAppear:(BOOL)animated // or successfully swiped back [self.screenView notifyAppear]; [self notifyTransitionProgress:1.0 closing:NO goingForward:_goingForward]; + } else { + [self.screenView notifyGestureCancel]; } _isSwiping = NO; @@ -1275,6 +1291,7 @@ @implementation RNSScreenManager RCT_EXPORT_VIEW_PROPERTY(onTransitionProgress, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onWillAppear, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onWillDisappear, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onGestureCancel, RCTDirectEventBlock); #if !TARGET_OS_TV RCT_EXPORT_VIEW_PROPERTY(screenOrientation, UIInterfaceOrientationMask) diff --git a/src/fabric/ScreenNativeComponent.ts b/src/fabric/ScreenNativeComponent.ts index 9506573c61..acde0f52bb 100644 --- a/src/fabric/ScreenNativeComponent.ts +++ b/src/fabric/ScreenNativeComponent.ts @@ -62,6 +62,7 @@ export interface NativeProps extends ViewProps { onWillAppear?: BubblingEventHandler; onWillDisappear?: BubblingEventHandler; onTransitionProgress?: BubblingEventHandler; + onGestureCancel?: BubblingEventHandler; sheetAllowedDetents?: WithDefault; sheetLargestUndimmedDetent?: WithDefault; sheetGrabberVisible?: WithDefault; diff --git a/src/index.native.tsx b/src/index.native.tsx index 5190340922..252e27dd0e 100644 --- a/src/index.native.tsx +++ b/src/index.native.tsx @@ -285,6 +285,7 @@ class InnerScreen extends React.Component { children, isNativeStack, gestureResponseDistance, + onGestureCancel, ...props } = rest; @@ -340,6 +341,12 @@ class InnerScreen extends React.Component { { useNativeDriver: true } ) } + onGestureCancel={ + onGestureCancel ?? + (() => { + // for internal use + }) + } > {!isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed children diff --git a/src/native-stack/types.tsx b/src/native-stack/types.tsx index f307fe4f3f..1edc235add 100644 --- a/src/native-stack/types.tsx +++ b/src/native-stack/types.tsx @@ -37,6 +37,10 @@ export type NativeStackNavigationEventMap = { * Event which fires when a transition animation ends. */ transitionEnd: { data: { closing: boolean } }; + /** + * Event which fires when a swipe back is canceled on iOS. + */ + gestureCancel: { data: undefined }; }; export type NativeStackNavigationProp< diff --git a/src/native-stack/views/NativeStackView.tsx b/src/native-stack/views/NativeStackView.tsx index 98b85c5d89..a6dc7670fd 100644 --- a/src/native-stack/views/NativeStackView.tsx +++ b/src/native-stack/views/NativeStackView.tsx @@ -319,6 +319,12 @@ const RouteView = ({ target: stateKey, }); }} + onGestureCancel={() => { + navigation.emit({ + type: 'gestureCancel', + target: route.key, + }); + }} > ) => void; + /** + * A callback that gets called after swipe back is canceled. + */ + onGestureCancel?: (e: NativeSyntheticEvent) => void; /** * An internal callback that gets called when the native header back button is clicked on Android and `enableNativeBackButtonDismissal` is set to `false`. It dismises the screen using `navigation.pop()`. *