26

I have a gesture recognizer on a UIScrollView, however it hardly ever gets called as the UIScrollView eats all the gestures.

I partially got around this issue with this line: [scrollView.panGestureRecognizer requireGestureRecognizerToFail:rightSwipe]; however, this line results in my recognizer always being accepted (the desired behavior) and the scroll view not scrolling.

That is, when you scroll, the recognizer is accepted but the view doesn't scroll.

How can I get around this, or is there an alternate solution?

Thanks!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Phillip
  • 1,205
  • 3
  • 15
  • 22

6 Answers6

25

Make a subclass of UIScrollView. Add this method in your new subclass

- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer 
{
    return YES;
}

Make your scrollView class to your new scrollview subclass.

Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
  • If anyone comes here because your UIPanGestureRecognizer inside UIScrollView is not working after updating to Swift 5 (when it actually worked before) - this is the solution. – Jan Erik Schlorf Mar 27 '19 at 10:17
11

The way that works for me is subclass UIScrollView and conform to the UIGestureRecognizerDelegate in that subclass. Then call this method.

class ATScrollView: UIScrollView, UIGestureRecognizerDelegate { 
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                           shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    } 
}
abe973t
  • 117
  • 2
  • 6
10

Don't forget to assign proper delegation to your gesture recognizer mechanism:

So, for example:

Set a .up direction gesture recognizer

let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.handleGesture(gesture:)))
        swipeUp.direction = .up
        swipeUp.delegate = self // set delegate
        self.addGestureRecognizer(swipeUp)

Set a .down direction gesture recognizer

let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.handleGesture(gesture:)))
        swipeDown.direction = .down
        swipeDown.delegate = self // set delegate
        self.addGestureRecognizer(swipeDown)

Also don't forget to conform to delegation:

YourViewController: UIGestureRecognizerDelegate

Set simultaneous recognition:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

Then move on to your logic...

Hope it helps!

Mert Kahraman
  • 445
  • 6
  • 10
  • I love that you solved the problem using the `UIGestureRecognizerDelegate` property. I personally think is the correct way to handle this and I've up-voted it. However I found the swipe up/down example confusing for some reason. At first I thought you were trying to manually implement scrolling. I recommend replacing the swipe up/down example with a UIPanGestureRecognizer example to keep it simple and focus on the delegate concept. Thanks. – Jeffrey Fulton Aug 24 '18 at 04:21
1

Swift method,
Need to add UIGestureRecognizerDelegate to your class:

class ViewController: UIViewController, UIGestureRecognizerDelegate {

Need to set its delegate to self:

override func viewDidLoad() {
    super.viewDidLoad()
    var scrollView = UIScrollView(frame: self.view.frame)
    scrollView.delegate = self
}

Add this part in your class methods to active simultaneous gestures:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}
Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
  • i have this exact problem, but nothing i try seems to work. could you please take a look at my code? – stanley Mar 21 '15 at 08:28
  • do you have a question? – Esqarrouth Mar 21 '15 at 08:43
  • 1
    i'm building a "google now" type interface and i have "cards" which are all uiviews inside a uiscrollerr to either scroll or to swipe off the view, but never both at the same time. i spent the last 3 hrs trying to implement examples from stackoverflow but it never works :( – stanley Mar 21 '15 at 08:48
  • i 've tried http://stackoverflow.com/questions/25355638/uibutton-stops-uiscrollview-scrolling-in-swift – stanley Mar 21 '15 at 08:53
  • and this http://stackoverflow.com/questions/25800325/ios-8-buttons-in-horizontal-scroll-view-intercepting-pan-event-scroll-does-n/25900859#25900859 – stanley Mar 21 '15 at 08:53
  • and this http://stackoverflow.com/questions/25355638/uibutton-stops-uiscrollview-scrolling-in-swift – stanley Mar 21 '15 at 08:55
  • all the suggestions start to repeat after a while. this is my first time getting the hang of ios gestures and stuff, i honestly don't know how to solve this problem – stanley Mar 21 '15 at 08:58
  • post your own code in your own question. old answers dont generally work in ios.. – Esqarrouth Mar 21 '15 at 09:46
  • 21
    The scrollView delegate is a scrollViewDelegate, not any kind of gesture delegate. With just this setup you've shown, "shouldRecognizeSimultaneouslyWithGestureRecognizer" will never get called. – snakeoil Sep 16 '16 at 21:42
  • this will never compile since ViewController doesn't implement scrollviewDelegate. – Tommy Sadiq Hinrichsen Oct 24 '17 at 14:44
1

Rather than trying to work with TWO pan gestures (UIScrollView uses one internally already), just hijack the scrollview's built in pan gesture recognizer to make a second callback to your custom method as well.

After you set up yourScrollView, add this:

[yourScrollView.panGestureRecognizer addTarget:self action:@selector(scrollViewPan:)];

Then capture the scrollview's built in pan with the selector:

-(void)scrollViewPan:(UIPanGestureRecognizer *)sender {
    
    CGPoint translatedPoint = [sender translationInView:sender.view.superview];
    NSLog(@"%f,\t%f", translatedPoint.x, translatedPoint.y);
    
}

Make sure, of course, you have <UIGestureRecognizerDelegate> set in your .h

Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
0

Forgive me for this tangent but I'm posting this answer just in case anyone else makes the same misdiagnosis I did. If like me the above solutions aren't working for you, make sure you're not trying to attach your UISwipeGestureRecognizer to more than one view. It can only work with one view, and that will be view to which it was most recently attached!

biomiker
  • 3,108
  • 1
  • 29
  • 26