17

I use UIScrollView to make large-sized (larger than 320px) UI on iPhone.

I made an instance of UIScrollView and added some subviews on it. The problem is that I want to enable scrolling only when user touches outside of subviews, stop scrolling when user touches one of subviews.

I read documents and tried to find samples but I can't find good hint. If you have any idea, please help me.

ramsvidor
  • 1,480
  • 1
  • 14
  • 19
fish potato
  • 5,319
  • 6
  • 27
  • 32
  • @Chris Hanson: Why did you remove iPhone and UIScrollView from the tags? Those are the correct tags - cocoa-touch can be added, but should not stand alone here. – Emil Jun 23 '10 at 09:34
  • I didn't remove or edit those tags :( – fish potato Jun 24 '10 at 04:51
  • Because back when I made that edit, polluting the tag space with names of classes and redundant information wasn't a good idea. It still isn't, I just gave up on fixing that crap. – Chris Hanson Jul 01 '10 at 04:27

6 Answers6

36

UIScrollView has a scrollEnabled property that allows you to disable scrolling programatically. It also has a delegate (UIScrollViewDelegate) that allows you to see events such as scrolling starting/ending. Seems that you should be able to cook something up with those options combined in some way.

Stephen Darlington
  • 51,577
  • 12
  • 107
  • 152
8

The property you're really interested in -- and I'm actually testing this out right now, because I have the same problem you do -- is canCancelContentTouches.

If the value of this property is NO, the scroll view does not scroll regardless of finger movement once the content view starts tracking.

If this doesn't give you the results you want, subclass UIScrollView and override the touchesShouldBegin:withEvent:inContentView method, which is what the accepted answer suggests.

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
5

If you want to detect touches inside any of the subviews of the UIScrollView, you will have to subclass UIScrollView and override the touchesShouldBegin and touchesShouldCancelInContentView methods which are specifically created for this purpose.

Other than this, there is no way you can identify touches in the subviews as UIScrollView tends to handle all touches itself and doesn't pass them to its subviews.

All the best.

lostInTransit
  • 70,519
  • 61
  • 198
  • 274
  • Thank you for your answer. I tried to override touchesShouldBegin / touchesShouldCancel... methods but these methods were never called. Maybe something is wrong with my coding but I can stop scrolling by overriding touchesBegin/touchesMoved/touchedEnded methods. Thank you! – fish potato Jan 06 '09 at 14:27
  • Are you doing this in a subclass on UIScrollView? The methods will be called only then. So instead of your UIViewController using a UIView as its view it should use a subclass of UIScrollView as the view – lostInTransit Jan 07 '09 at 11:36
  • Yes, i tried this in a subclass of UIScrollView. I got some problem in my implement(only overriding touchesBegan...), so I keep trying to find the reason why touchesShouldBegin method was never called. – fish potato Jan 10 '09 at 02:37
  • I had the same problem. For me, it was due to the underlying subview (UIImageView) not having the userInteractionEnabled set to YES. – Ken Joyner Mar 09 '11 at 00:23
  • This answer is wrong, shouldn't be the accepted answer. You DO NOT have to subclass UIScrollView at all. There is a bug in UIScrollView that breaks UIViews - if you fix your UIView's, all touches work perfectly (they drop through to the UIViewController) AND the scrollview continues working as expected. The secret is to set nextResponder correctly in the UIView subclass when Apple accidentally deletes it – Adam Aug 31 '12 at 15:17
5

The simplest way to do this is set delaysContentTouches to NO for the scrollview. That way the default behavior is set so that anything which is a UIControl will pass the touches on to the control immediately and everything else will scroll.

aheze
  • 24,434
  • 8
  • 68
  • 125
Dave Verwer
  • 6,140
  • 5
  • 34
  • 30
2

You can also sublcass UIScrollViewController and override the touchesBegan, touchesMoved and touchesEnded methods. If your implementation never calls the superclass implementation, then it won't scroll.

Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75
1

My problem was having a view that a user could draw in but the scroll view was hijacking the touch and scrolling instead of drawing. This is my working solution:

In my drawing view I have:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    // Disable scroll on touch begin
    setContainerScrollingEnabled(false)
    super.touchesBegan(touches, with: event)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

    // Reenable scroll on touch end
    setContainerScrollingEnabled(true)
    super.touchesEnded(touches, with: event)
}

private func setContainerScrollingEnabled(_ enabled: Bool) {

    // loop up superviews to find the scrollview
    func scrollingSuperview(of view: UIView) -> UIScrollView? {
        guard let superview = view.superview else { return nil }
        if let scrolling = view.superview as? UIScrollView { return scrolling }
        return scrollingSuperview(of: superview)
    }

    // and switch on/off scrolling
    let scrollView = scrollingSuperview(of: self)
    scrollView?.isScrollEnabled = enabled
}

and in the view controller containing the scroll view I have:

// Not sure what exactly this does, but I think it allows the drawing view to have precedence. This was the missing piece of the puzzle.
scrollView.delaysContentTouches = false
Adam Waite
  • 19,175
  • 22
  • 126
  • 148