2

I have a view hierarchy that is layed out as follows:

parentView

 scrollView

    contentViewA

    containerView

         contentViewB

         contentViewC

I want contentViewB to respond to touches. Unfortunately scrollView makes this almost impossible because it tries to ingest the touches itself making touch response of contentViewB spotty.

So, instead, I want to intercept all touches in the parentView, manipulate contentViewB directly, and then pass the touches on to scrollView so it can do its thing.

Can someone please show me the correct way to pull this off?

Thanks in advance.

Cheers, Doug

UPDATE:

I did a bit more digging and found the property canCancelContentTouches which seems to work wonders. I'm using IB so I unchecked "Cancellable Content Touches" in IB - first tab of the Scroll View Attribute Inspector. Now, when I run the app, touches appear to be arriving at contentViewB reliably.

Here's how the UIScrollView docs describe this property:

Discussion If the value of this property is YES and a view in the content has begun tracking a finger touching it, and if the user drags the finger enough to initiate a scroll, the view receives a touchesCancelled:withEvent: message and the scroll view handles the touch as a scroll. If the value of this property is NO, the scroll view does not scroll regardless of finger movement once the content view starts tracking.

Rather opaque huh? Anyway, it seems to work.

dugla
  • 12,774
  • 26
  • 88
  • 136
  • having read your update I think I may have misunderstood your original problem. So you want to detect "tap"-like touches on subviews, but drag-touches should cause scrolling. In that case canCancelContentTouches should be the way to go (actually I thought that was set by default). That's pretty much what I do in my game, vConqr (www.vconqr.com) – philsquared Nov 04 '09 at 18:32
  • Sorry to confuse you Phil. Actually contentViewB detects both a single tap as well as a vertical - y-axis - drag gesture. I may not have made this clear, but I've implemented a bare bones vertical scroller in contentViewB since I don't need the complexity - and headache - of nested scrollViews. The scrollView superview in my diagram is configured to drag/zoom horizontally - along the x-axis. Thanks for the assist Phil. – dugla Nov 05 '09 at 13:53

2 Answers2

2

To stop the scroll view from intercepting the touch events, set the userInteractionEnabled property like so:

scrollView.userInteractionEnabled = NO;
philsquared
  • 22,403
  • 12
  • 69
  • 98
  • Thanks Phil. Which view in the hierarchy should toggle scrollView.userInteractionEnabled? – dugla Nov 04 '09 at 16:31
  • Also, via what method - hitTest:?, touchesBegan:withEvent:?. Thanks. – dugla Nov 04 '09 at 16:36
  • Who owns the scroll view (usually a view contoller). When it's first created and set as a subview is usually when you'd do it - or do you need to update this at different times? – philsquared Nov 04 '09 at 18:04
  • Indeed a viewController owns the scrollView. The challenge is I need to toggle the scrollView.userInteractionEnabled. This is because I have configured the scrollView to only scroll horizontally but have implemented a trivial vertical scroller from scratch for contentViewB. – dugla Nov 04 '09 at 18:19
  • ok. At some point you must know when to switch. You can then call a method on the view controller (which you will implement) that will toggle the property. Best to store the scroll view as a member of the view controller if you're not already doing so. – philsquared Nov 04 '09 at 18:28
  • see my comment to your original question – philsquared Nov 04 '09 at 18:33
  • Phil, See the update to the question I wrote. What do you think of this approach? – dugla Nov 04 '09 at 18:46
  • Ok, lets see if I've got this. If I go with your approach, I would set scrollView.userInteractionEnabled = NO in my implementation touchesBegan: and scrollView.userInteractionEnabled = YES in touchesEnded:. I would implement the touch sequence methods in some reasonable class such as parentView. Sound about right? – dugla Nov 04 '09 at 18:50
  • no, now that I have a slightly better understanding of what you're trying to do (I think), I don't think this is the right approach. The ability to intepret touches by a scroll view and its subviews, depending on whether you are actually scrolling, is baked in. I don't have access to my dev machine at the moment to check the specifics, but will have a look later if you're still having trouble – philsquared Nov 05 '09 at 09:27
1

Another way of doing this is to add another subview to your ui so it looks like so :

parentView
  scrollView
    contentViewA
      containerView
        contentViewB
        contentViewC
  touchGrabber

and, in touchGrabber, detect all the touches that you want (by subclassing UIView)

This is more complicated than Phil Nash's solution but has the advantage that you can add/remove other views from your parentView without having to deal with their userInteractionEnabled value - this is useful if you have a third party library adding views for example.

However, if you definately only going to have the scrollView, Phil Nash's answer is the way forward!

Thanks,

Sam

Community
  • 1
  • 1
deanWombourne
  • 38,189
  • 13
  • 98
  • 110