1

I have a subclassed UIView that implements touchesBegan, touchesMoved, touchesEnded. All is working well. Then, I added a UIScrollview as subview to this UIVIew. Touches made within the UIScrollview are not forwarded back up the responder chain to the parent view, and so my UIView's touch methods are never called (they are called for touches made outside of the scrollview just fine).

I created a new UIScrollView category like so:

@implementation UIScrollView (forwardTouches)

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

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self.nextResponder touchesBegan: touches withEvent:event];
        [self setScrollEnabled:NO];
        NSLog(@"touchesBegan");
    }
    else{
        NSLog(@"touchesBegan-else");
        [super touchesEnded: touches withEvent: event];
    }
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self.nextResponder touchesMoved: touches withEvent:event];
        NSLog(@"touchesMoved");
    }
    else{
        NSLog(@"touchesMoved - else");
        [super touchesEnded: touches withEvent: event];
    }
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self.nextResponder touchesEnded: touches withEvent:event];
        [self setScrollEnabled:YES];
        NSLog(@"touchesEnded");
    }
    else{
        NSLog(@"touchesEnded - else");
        [super touchesEnded: touches withEvent: event];
    }
}

This actually seems to work as intended on the iPhone (which would be: if the user drags his finger across the scrollview, it scrolls, but if he places it on the view and does not move it, the parent view's touchesBegan method fires), but it does not work on the iPad. Presumably, self.dragging needs more time on the iPad, but I'm struggling to figure out a way to solve this reliably. I'm sure I'm missing something very obvious. Thanks in advance!

EDIT WITH POSSIBLE SOLUTION:

I came up with the code below after some trial and error. It seems to be working well on iPhone and iPad. Any reasons why using this may be a bad idea?

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

    NSArray * myDataArray = [NSArray arrayWithObjects:touches, event, nil];

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self performSelector:@selector(forwardTouches:) withObject:myDataArray afterDelay:0.5];
    }
    else{
        NSLog(@"touchesBegan-else");
        [super touchesEnded: touches withEvent: event];
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }
}

-(void)forwardTouches:(NSArray *)array{
    NSSet * mySet = [array objectAtIndex:0];
    UIEvent * myEvent = [array objectAtIndex:1];

    if (!self.dragging){
        [self.nextResponder touchesBegan:mySet withEvent: myEvent];
        [self setScrollEnabled:NO];
    }
}

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

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self.nextResponder touchesMoved: touches withEvent:event];
        NSLog(@"touchesMoved");
    }
    else{
        NSLog(@"touchesMoved - else");
        [super touchesEnded: touches withEvent: event];
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    // If not dragging, send event to next responder
    if (!self.dragging){
        [self.nextResponder touchesEnded: touches withEvent:event];
        [self setScrollEnabled:YES];
        NSLog(@"touchesEnded");
    }
    else{
        NSLog(@"touchesEnded - else");
        [super touchesEnded: touches withEvent: event];
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }
}
Community
  • 1
  • 1
user1459524
  • 3,613
  • 4
  • 19
  • 28
  • Are you just trying to capture the tap event in the parent view without interrupting scrolling in the scroll view? – hukir Dec 03 '13 at 04:50
  • I am trying to get an either-or scenario. If the user presses his finger on the screen and does not move it, I'd like my touches methods from the UIView to fire, and the scrollview to NOT scroll. If the user presses the finger on the screen and moves it, I'd like the UIScrollview to scroll, and the touches methods of the UIView NOT to fire. – user1459524 Dec 03 '13 at 13:14

1 Answers1

0

The code above is not a good plan. If you want to super view got a chance to handle the touch which appear in the subview , you should use gesture , not changing the forwarding of the touch , It's dangerous.

John
  • 11
  • 3
  • I can't change the code of the superview to use gestures, for a number of reasons. Can I use a gesturerecognizer to intercept the touch in the scrollview, and forward it to the touches methods of the superview instead? – user1459524 Dec 03 '13 at 13:23
  • May UITapGestureRecognizer satisfied with your need? Add a UITapGestureRecognizer to the superview; when user taps , call the superview , when user scrolls , call the scrollView; – John Dec 04 '13 at 11:37
  • Yes, that could work, but I am not sure how to implement this in code. – user1459524 Dec 04 '13 at 12:45
  • To elaborate - I can easily add a UIGestureRecognizer to the viewcontroller. But I don't know how to pass the call to the (custom) view, and how do I translate it into its touches... methods? – user1459524 Dec 04 '13 at 16:32
  • Nevermind, I figured it out. I had to put the UIGestureRecognizer into the UIView's awakefromnib, not the controller. – user1459524 Dec 04 '13 at 16:52
  • Yes , you can set the gesture's "target" to the view, also the SEL to the view's Selector. – John Dec 05 '13 at 01:47