Skip to content

Fix: Navigation gesture gestureResponseDistance failure #1016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ For advanced usage please take a look into our [example project](https://github.
| `offscreenPageLimit: number` | Set the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed. Defaults to RecyclerView's caching strategy. The given value must either be larger than 0. | Android |
| `overdrag: boolean` | Allows for overscrolling after reaching the end or very beginning or pages. Defaults to `false` | iOS |
| `layoutDirection: ('ltr' / 'rtl' / 'locale')` | Specifies layout direction. Use `ltr` or `rtl` to set explicitly or `locale` to deduce from the default language script of a locale. Defaults to `locale` | both |
| `allowNavigationBackGesture: boolean` | Allows navigation swipe-back gestures to pass through when PagerView is at index 0. If `overdrag` is set to `true`, it will have higher priority. Defaults to `false` | iOS |

| Method | Description | Platform |
| ------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: |
Expand Down Expand Up @@ -242,6 +243,7 @@ const pageScrollHandler = usePageScrollHandler({
```

## usePagerView Hook Usage

The `usePagerView` hook is a convenient way to manage the state and control the behavior of the `<PagerView />` component. It provides functions and variables to interact with the pager, such as navigating between pages and enabling/disabling scrolling.

Below is an example of how to use the usePager hook:
Expand Down Expand Up @@ -294,7 +296,8 @@ export function PagerHookExample() {
);
}
```
### How the Example Works:

### How the Example Works

- **Pager View Setup**: The `AnimatedPagerView` component wraps `PagerView` in React Native's animation capabilities. It accepts multiple props from the `usePager` hook, such as `overdragEnabled`, `scrollEnabled`, `onPageScroll`, `onPageSelected`, and others to manage pager behavior.

Expand All @@ -304,7 +307,6 @@ export function PagerHookExample() {

The `usePager` hook makes it easy to handle pagination with dynamic views. This example demonstrates how to set up a simple paginated interface where users can scroll through pages, interact with page elements, and control the pager with external navigation.


## License

MIT
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ class PagerViewViewManager : ViewGroupManager<NestedScrollableHost>(), RNCViewPa
}
}

@ReactProp(name = "allowNavigationBackGesture")
override fun setAllowNavigationBackGesture(view: NestedScrollableHost?, value: Boolean) {
return
}

@ReactProp(name = "overdrag")
override fun setOverdrag(view: NestedScrollableHost?, value: Boolean) {
return
Expand Down
49 changes: 47 additions & 2 deletions ios/RNCPagerViewComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

using namespace facebook::react;

@interface RNCPagerViewComponentView () <RCTRNCViewPagerViewProtocol, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate>
@interface RNCPagerViewComponentView () <RCTRNCViewPagerViewProtocol, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate>

@property(nonatomic, strong) UIPageViewController *nativePageViewController;
@property(nonatomic, strong) NSMutableArray<UIViewController *> *nativeChildrenViewControllers;
Expand All @@ -28,6 +28,8 @@ @implementation RNCPagerViewComponentView {
NSInteger _destinationIndex;
BOOL _overdrag;
NSString *_layoutDirection;
BOOL _allowNavigationBackGesture;
UIPanGestureRecognizer *_navGestureRecognizer;
}

// Needed because of this: https://github.com/facebook/react-native/pull/37274
Expand Down Expand Up @@ -76,6 +78,8 @@ - (instancetype)initWithFrame:(CGRect)frame
_destinationIndex = -1;
_layoutDirection = @"ltr";
_overdrag = NO;
_allowNavigationBackGesture = NO;
_navGestureRecognizer = nil;
}

return self;
Expand All @@ -88,6 +92,27 @@ - (void)willMoveToSuperview:(UIView *)newSuperview {
}
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (!_allowNavigationBackGesture) {
return NO;
}

if (otherGestureRecognizer == self->scrollView.panGestureRecognizer) {
UIPanGestureRecognizer* panGestureRecognizer = (UIPanGestureRecognizer*) gestureRecognizer;
CGPoint velocity = [panGestureRecognizer velocityInView:self];
BOOL shouldPagerGestureFail = !_overdrag && !scrollView.isDecelerating && _currentIndex == 0 && velocity.x > 0;
if (shouldPagerGestureFail) {
self->scrollView.panGestureRecognizer.enabled = NO;
return NO;
} else {
self->scrollView.panGestureRecognizer.enabled = self->scrollView.scrollEnabled;
}
} else {
self->scrollView.panGestureRecognizer.enabled = self->scrollView.scrollEnabled;
}

return YES;
}

#pragma mark - React API

Expand Down Expand Up @@ -121,11 +146,12 @@ -(void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:_layoutMetrics];
}


-(void)prepareForRecycle {
[super prepareForRecycle];
_nativePageViewController = nil;
_currentIndex = -1;
_allowNavigationBackGesture = NO;
[self cleanupGestureRecognizer];
}

- (void)shouldDismissKeyboard:(RNCViewPagerKeyboardDismissMode)dismissKeyboard {
Expand Down Expand Up @@ -173,6 +199,18 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props oldProps:(cons
_overdrag = newScreenProps.overdrag;
}

if (newScreenProps.allowNavigationBackGesture != _allowNavigationBackGesture) {
_allowNavigationBackGesture = newScreenProps.allowNavigationBackGesture;

if (_allowNavigationBackGesture) {
_navGestureRecognizer = [[UIPanGestureRecognizer alloc] init];
_navGestureRecognizer.delegate = self;
[self addGestureRecognizer:_navGestureRecognizer];
} else {
[self cleanupGestureRecognizer];
}
}

[super updateProps:props oldProps:oldProps];
}

Expand Down Expand Up @@ -407,6 +445,13 @@ - (BOOL)isLtrLayout {
return [_layoutDirection isEqualToString: @"ltr"];
}

- (void)cleanupGestureRecognizer {
if (_navGestureRecognizer) {
[self removeGestureRecognizer:_navGestureRecognizer];
_navGestureRecognizer = nil;
}
}

- (std::shared_ptr<const RNCViewPagerEventEmitter>)pagerEventEmitter
{
if (!_eventEmitter) {
Expand Down
1 change: 1 addition & 0 deletions src/PagerViewNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface NativeProps extends ViewProps {
overScrollMode?: WithDefault<'auto' | 'always' | 'never', 'auto'>;
overdrag?: WithDefault<boolean, false>;
keyboardDismissMode?: WithDefault<'none' | 'on-drag', 'none'>;
allowNavigationBackGesture?: WithDefault<boolean, false>;
onPageScroll?: DirectEventHandler<OnPageScrollEventData>;
onPageSelected?: DirectEventHandler<OnPageSelectedEventData>;
onPageScrollStateChanged?: DirectEventHandler<OnPageScrollStateChangedEventData>;
Expand Down
Loading