10
votes

Note: I already read some questions about the UIScrollView sending touches to the subviews (this included and although I have up voted, it's not working as I intended anymore).

What I have: I have a UIScrollView with a Custom UIView (let's call it A) inside which covers the entire UIScrollView. I am also allowed to put other custom UIViews inside the A.

On the code I am doing this:

[scrollView setDelaysContentTouches:YES];
scrollView.canCancelContentTouches = NO;

What is happening: At the moment my only issue is that, if I want to move a subview inside A, I have to touch it, wait, and then move it. Exactly as stated here:

Now, the behaviour changes depending on the "length in time" of the first touch on the UIView. If it's short, then the relative dragging is managed as it was a scroll for the UIScrollView. If it's long, then I'm getting the touchesMoved: events inside my UIView.

What I want: The subviews inside A should always receive priority and I shouldn't have to touch and wait. If I touch A and not a subview of it, I want the UIScrollView to receive the touches, like panning and moving around (the contentSize is bigger than the frame).


Edit 1.0

The only reason for me to have this A view inside a generic UIScrollView, is because I want to be able to zoom in/out on the A view. So I am doing the following:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return customView; // this is the A view
}

In the beginning I didn't had the A view inside the UIScrollView and the only thing I did was adding the A as a subView of my UIViewController's root view and everything went well. If there is another way to enable zoom in/out I will gladly accept the answer.

4
Just quick googling, tried to override scrollView's hitTest ? herbert-siojo.com/2011/04/01/…A-Live
Already seen this suggestion, doesn't work.Rui Peres
Did you try to add UIGestureRecognizer to the scrollview and then check touch is locationInView is in custom uiview ...Mina Nabil
The A view is covering all the screen. So the A screen is always going to be returned.Rui Peres

4 Answers

8
votes

Note: Thank you all for your contributions, specially to Aaron Hayman.

I was able to figure it out by doing the following on the UIScrollView sub-class I had:

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
   CGPoint pointOfContact = [gestureRecognizer locationInView:self];

    // The view with a tag of 200 is my A view.
    return (![[self hitTest:pointOfContact withEvent:nil] isEqual:[self viewWithTag:200]]);
}
4
votes

I haven't tested this, but I believe how you are handling the touch events in View A (or it's subviews) will determine how touch events are passed on. Specifically, if you're trying to use the methods: touchesBegan, touchesMoves, touchesEnded, etc instead of a UIGestureRecognizer you won't receive the touches in the way you want. Apple design the UIGestureRecognizer to handle problems like the one you're facing. Specifically, the UIScrollView uses UIPanGestureRecognizer to handle the scrolling. If you add a UIPanGestureRecognizer to each of the subviews of View A any "panning" that occurs on one of those subviews should be sent to that subview instead of the UIScrollView. However, if you're simply using the "raw" touches methods, the UIPanGestureRecognizer in UIScrollView will never be cancelled.

In general, it's almost always best to use a UIGestureRecognizer instead of processing the touches directly in the view. If you need touches processed in a way that no standard UIGestureRecognizer can provide, subclass UIGestureRecognizer and process the touches there. That way you get all the the functionality of a UIGestureRecognizer along with your own custom touch processing. I really think Apple intended for UIGestureRecognizer to replace most (if not all) of the custom touch processing code that developers use on UIView. It allows for code-reuse and it's a lot easier to deal with when mitigating what code processes what touch event.

0
votes

Jacky, I needed a similar thing: Within a building plan (your A, in my case a subclass of UIScrollView), let the user place and resize objects (call them Bs). Here's a sketch of what it took me to get at this behavior:

  • In the superview's (A) initWithFrame: method, set these two:

    self.canCancelContentTouches = YES;
    self.delaysContentTouches = NO;
    

    This will ensure taps on B are immediately routed to the Bs.

  • In the embedded B, stop the superview A from cancelling taps, so it does not interfere with a gesture started on the B.

    In the touchesBegan: method, search the view hierarchy upwards (using superview property of the views) until you find a UIScrollView, and set its canCancelContentTouches to NO. Remember the superview you changed, and restore this property in the touchesEnded and touchesCancelled methods of B.

I'd be interested whether this works for you as well. Good Luck!

nobi

0
votes

I think you had better use "touchesBegan,touchesMoved,touchesEnded" to pass the event.

you can do like this:

you should make a mainView . It has 2 property. One is yourScrollView A , and One is yourCustomView.

`[yourScrollView addSubviews:yourCustomView];
[mainView addSubviews:yourScrollView];`

and then write your touches method in the mainView.m like this (ignor the scrollView statment)

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
   if ([[touches allObjects] isKindOfClass:[yourCustomView class]]) 
   {

        //do whatever you want
   }

}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
   if ([[touches allObjects] isKindOfClass:[yourCustomView class]]) 
   {
       //do whatever you want
   }
}

The last step: pass the event to the subview of the scrollView(your A).

#import "yourScrollView.h"


@implementation yourScrollView


- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code.
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesMoved:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesEnded:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesEnded:touches withEvent:event];
}

- (void)dealloc {
    [super dealloc];
}


@end 

wish to help you