46

I have a scroll view that used to scroll when it didn't have buttons all over it. Now it does, and when dragging the mouse (on simulator) nothing happens (i think because the buttons are being pushed). How can I make this right?

Juan Boero
  • 6,281
  • 1
  • 44
  • 62
DanielaM
  • 591
  • 1
  • 5
  • 6
  • 1
    Have you tried it on a physical device, there are several differences between devices and the simulator, and the mouse and multi-touch is one of the biggest! – Tom H Aug 18 '10 at 13:37

5 Answers5

104

This is happening because UIButton subviews of the UIScrollView (I assume buttons are added as subviews in your case) are tracking the touches and not the scroll view. UIScrollView method touchesShouldCancelInContentView is the key here. According to its description: "The default returned value is YES if view is not a UIControl object; otherwise, it returns NO.", i.e. for UIControl objects (buttons), UIScrollView does not attempt to cancel touches which prevents scrolling.

So, to allow scrolling with buttons:

  1. Make sure UIScrollView property canCancelContentTouches is set to YES.
  2. Subclass UIScrollView and override touchesShouldCancelInContentView to return YES when content view object is a UIButton, like this:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
    if ( [view isKindOfClass:[UIButton class]] ) {
        return YES;
    }

    return [super touchesShouldCancelInContentView:view];
}
Zev Eisenberg
  • 8,080
  • 5
  • 38
  • 82
Roman Kishchenko
  • 1,894
  • 1
  • 15
  • 12
  • thank you for your answer. I changed the scroll having images to simulate buttons (and tracking touches to know when to call a method) I will try your solution though and see how it works. thanks! – DanielaM Aug 25 '10 at 09:07
  • 4
    @DanielaM If this marvelous answer has helped you as it really seems, it would be good to accept it. – iHunter Sep 13 '13 at 12:02
  • **bold** @Roman K - You are my HERO! I've spent 2 days reading in many posts\forums about this overlapping problem but no tweak or solution really worked. Your solution really works!! Cheers :-) – Shvalb Oct 07 '13 at 03:51
  • 1
    It still doesn't work for me. touchesShouldCancelInContentView is never called, unless I set the delay to NO, but in the latter case, there is no scroll at all. – Michael Chourdakis Dec 01 '13 at 20:34
  • Thank you so much! I've spent hours on this. – SeanR Feb 26 '14 at 06:00
  • I really appreciate this insight. – mxcl Jul 05 '14 at 22:46
  • Appreciate this one as well – barfoon Aug 01 '14 at 03:41
  • Thanks a lot! This solved the issue for me. Note that I had UIPanGestureRecognizer recognizer on the subview and I needed to delete it in order to make this work. – sensimevanidus Aug 05 '14 at 22:46
  • Can you provide an example for touchesShouldCancelInContentView ? – danielsalare Aug 29 '14 at 03:56
  • 1
    I added an example. Note that I would actually check for `UIControl` instead of `UIButton`, in case a slider or stepper or switch showed up at some point. – Zev Eisenberg Aug 29 '14 at 17:38
  • 3
    The issue persists again in iOS 8 and `touchesShouldCancelInContentView` is not working anymore. But, the scrolling stuck only sometimes. Not always. – Min Soe Sep 22 '14 at 08:06
  • @MinSoe What did you do for that? – Bhumit Mehta Oct 13 '14 at 10:00
  • This [answer](http://stackoverflow.com/a/25900859/206592) helps me solve the problem. I think iOS 8 is having a lot of issue despite of having not a lot of changes in UI because of SWIFT – Min Soe Oct 14 '14 at 01:49
  • @Roman Kishchenko what if the buttons aren't added as subviews of the scrollview? – Tim Jan 10 '18 at 07:09
18

I founded this question looking for the swift solution for this problem, I "translated" it like this:

Swift 5

class UIButtonScrollView: UIScrollView {

    override func touchesShouldCancel(in view: UIView) -> Bool {
        if view.isKind(of: UIButton.self) {
          return true
        }

        return super.touchesShouldCancel(in: view)
    }    
}

hope this could help

mehmetkoca
  • 124
  • 1
  • 7
pugia
  • 442
  • 1
  • 4
  • 11
7

Swift 3 Solution

override func touchesShouldCancel(in view: UIView) -> Bool {
    if view is UIButton {
        return true
    }
    return super.touchesShouldCancel(in: view)
}
Gregg
  • 1,477
  • 1
  • 16
  • 17
1

One thing to try if you're seeing this in a simulator is to run on an actual phone. I couldn't scroll in the simulator but no prob on my phone.

William T. Mallard
  • 1,562
  • 2
  • 25
  • 33
0

In my case, I solved it with this way.

in ViewDidLoad

self.scrollView.panGestureRecognizer.delaysTouchesBegan = self.scrollView.delaysContentTouches;

in .m

- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
    if ([view isKindOfClass:[UIControl class]]) return YES;
    return NO;
}
User18474728
  • 363
  • 2
  • 11