Pageboy icon indicating copy to clipboard operation
Pageboy copied to clipboard

UITapGestureRecognizer does not work with scrollToPage

Open mattbarker016 opened this issue 6 years ago • 4 comments

I'm trying to set up a UITapGestureRecognizer that programmatically scrolls the PageViewController to the next page on tap. However, scrollToPage(...) doesn't work. I did some debugging, and this seems to stem from verifySafeToScrollToANewPage, where the command gets lost in guard statements because variables like the internal scrollView's isTracking being true causes the action to fail. I tried removing this temporarily, but still could not get the function to work properly.

Let me know if I can help debug or explain this issue better!

Swift 4.2 - Xcode 10

mattbarker016 avatar Sep 30 '18 16:09 mattbarker016

@mattbarker016 Will check it out!

msaps avatar Oct 08 '18 11:10 msaps

Hi, I had a similar problem when a child view controller contains a button. If the button tap is canceled by the user (e.g. touches down the button, then lifts the finger far enough from it), then the PageViewController's internal ScrollView is locked into the isTracking == true, preventing programmatic scrolling to another page. You can easily reproduce this problem in the demo by adding a button in the child view controller in storyboard.

I spent hours investigating how to prevent this isTracking lock down, without success. I tried to remove the isTracking check in your UIScrollView.isProbablyActiveInScroll extension, and it seems to work correctly for the following behaviors :

  • it no longer blocks programmatic page scroll after a button tap is canceled by the user (my use case)
  • it still blocks programmatic page scroll when a view is being dragged (e.g. running swipe/pan gesture)
  • it still blocks programmatic page scroll when the internal PageViewController is decelerating (e.g. during the end of a swipe gesture)

The main side effect of this change is that you can now programmatically scroll to another page while the user is still holding its finger over a button in a child view. I verified that if the user lifts its finger after the programmatic page scroll, it does not trigger the button tap, so this does not lead to unexpected behavior from the user's point of view.

On the other side this may be weird when auto-scrolling is enabled, for example if the user taps a button, and auto-scrolling is moving to the next page exactly while his finger is still touching the screen. Probability is very low but it may happen. Solving this case could be done by checking the isTracking flag only when auto-scrolling is enabled.

What's your opinion on this? May I submit a pull request for simply removing the isTracking check (but this would not address the auto-scrolling issue)?

Thanks for your excellent job on this library 😊

avangel avatar Feb 28 '19 09:02 avangel

By the way, I'm not sure this would address @mattbarker016 exact issue, so I may submit another dedicated issue if needed.

avangel avatar Feb 28 '19 09:02 avangel

I have had the same issue, here is my solution : first add this delegate method for your tap gesture in order to be able to tap buttons and other controls.

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Avoid buttons and other control UI
        if gestureRecognizer == pageChangeTapGestureRecognizer && touch.view is UIControl {
            return false
        }
        return true
    }

Then when using your "scrollToPage" method, use it in a DispatchQueue.main.async closure. This did the trick for me.

Claes34 avatar Apr 03 '19 12:04 Claes34