47

I have a UIScrollView to which I added a single tap gesture recognizer to show/hide some UI overlay using:

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[scrollView addGestureRecognizer:singleTap];

and:

- (void)handleTap:(UITapGestureRecognizer *)sender {
    // report click to UI changer
}

I added an easy table view to the bottom of the UIScrollView. Everything works right (scrolling both horizontally and vertically) but the problem is that taps are recognized only by the gesture recognizer (above), but not by the easy table view. If I remove The line that registers the gesture listener, everything works fine, the table view notices taps on itself.

It's as if the gesture recognizer function "eats" the tap events on the table view and doesn't propagate them downward.

Any help is appreciated

Anupdas
  • 10,211
  • 2
  • 35
  • 60
Itai Hanski
  • 8,540
  • 5
  • 45
  • 65

9 Answers9

82

This should solve your problem.
Detect touch event on UIScrollView AND on UIView's components [which is placed inside UIScrollView]
The idea is to tell the gesture recognizer to not swallow up the touch events. To do this you need to set singleTap's cancelsTouchesInView property to NO, which is YES by default.

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
singleTap.cancelsTouchesInView = NO;
[scrollView addGestureRecognizer:singleTap]; 
Community
  • 1
  • 1
zambrey
  • 1,452
  • 13
  • 21
  • 4
    I have similar issue ([my question](http://stackoverflow.com/questions/33105678/in-ios-button-tapping-not-fired-when-keyboard-is-visible)) but the solution does not works for me. Sometimes the event is fired, sometimes not. – new2ios Oct 16 '15 at 08:31
  • singleTap.cancelsTouchesInView = NO; Thanks Bro !! this solution helps me in table view to prevent itrrupting UItouch events – Abhishek Thapliyal Jun 16 '16 at 08:57
  • 1
    I was also having a similar issue, except mine was either highlighting the row (and not scrolling), or scrolling without highlighting the row first (only with a longer press it would work). I fixed it by adding the above "cancelsTouchesInView = false", as well as making sure that the tableView's "Can Cancel on Scroll" is checked and "Delay Touch Down" is unchecked (both easily found in the Interface Builder) – Merricat Oct 15 '18 at 23:11
15

Swift 3.0

 let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
 singleTap.cancelsTouchesInView = false
 singleTap.numberOfTapsRequired = 1
 scrollView.addGestureRecognizer(singleTap)

And the selector method be like.

@objc func handleTap(_ recognizer: UITapGestureRecognizer) {
  // Perform operation
}
asclepix
  • 7,971
  • 3
  • 31
  • 41
Jaydeep Vora
  • 6,085
  • 1
  • 22
  • 40
  • I do not understand why this solution is not working for me. If I do all of this with a UIView works fine but if I change UIView by UIScrollView, stop working. – Markus Oct 01 '20 at 13:33
10

I think the reason is that User Interaction Enabled is set to false for UIImageView. You should set it to true to enable tapping in it

RandyTek
  • 4,374
  • 3
  • 23
  • 32
6

You can set which objects are to be included/excluded for touches.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {            
   if (touch.view == [self view]) {
       return YES;
   }
   return NO;
}
arunit21
  • 670
  • 1
  • 11
  • 25
sangony
  • 11,636
  • 4
  • 39
  • 55
6

This worked for me in Swift 3 / Xcode 8

self.scrollView.touchesShouldCancel(in: ** the view you want the touches in **)
self.scrollView.canCancelContentTouches = false

Good luck!

nickromano
  • 918
  • 8
  • 16
hamishkeith
  • 186
  • 2
  • 5
3

Thanks @zambrey

Swift 2.2+ Version:

scrollView.delegate = self

let allowMultipleTouches = UITapGestureRecognizer(target: self, action: #selector(genderPressed))
allowMultipleTouches.numberOfTapsRequired = 1
allowMultipleTouches.cancelsTouchesInView = false

scrollView.addGestureRecognizer(allowMultipleTouches)

If your scroll view is in the Storyboard, don't forget to pin the Outlet in the view controller. In this example, scrollView is the Outlet of the UIScrollView.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
Nuno Vieira
  • 193
  • 2
  • 10
2

TapGestures worked for me. The swipe on the other hand, I had to disable the scrolling and it worked.

swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeToNewImage(_:)))
swipeLeftGesture.direction = .left
scrollView.addGestureRecognizer(swipeLeftGesture)

swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeToNewImage(_:)))
scrollView.addGestureRecognizer(swipeRightGesture)

scrollView.isScrollEnabled = false
nickromano
  • 918
  • 8
  • 16
Maria
  • 4,471
  • 1
  • 25
  • 26
  • When I do this, then my scrollView won't be able to scroll even programmatically (it compresses elements to fit in its container) – agirault Nov 25 '19 at 14:52
1

You can capture any kind of gestures in the UIscrollView. Make sure you also handle some of the default properties as well like set cancelsTouchesInView property to false, it is true by default. Also give some tag nos to your sub views to distinguish in selectors. & also enable their User interaction to true.

let tap = UITapGestureRecognizer(target: self, action:

selector(didTapByUser(_:)))

Ash
  • 5,525
  • 1
  • 40
  • 34
0

My code.

I checked all proposed solutions and any work for me. I do not understand why. I do not understand the reason.

class MyClass: UITableViewController {

override func viewDidLoad() {
    super.viewDidLoad()

let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))

scrollView.contentSize = CGSize(width: UIScreen.main.bounds.size.width, height: 800)

scrollView.isUserInteractionEnabled = true
scrollView.delegate = self

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(manageGesture))

tap.cancelsTouchesInView = false

tap.numberOfTapsRequired = 1

scrollView.addGestureRecognizer(tap)

scrollView.canCancelContentTouches = false

self.view.addSubview(scrollView)

}

@objc func manageGesture(){
    // Some action
}


}
Markus
  • 1,147
  • 16
  • 26