0

I am using the UICollectionView to display some products. In the custom UICollectionViewCell, there is a custom UIScrollView, where there are some images which allow the users to do a quick preview.

To forward tap gesture from the UIScrollView to the UICollectionViewCell, i override the touches related methods as below:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.dragging) {
        NSLog(@"Began ==>");
        [self.nextResponder touchesBegan:touches withEvent:event];
    }
    else {
        [super touchesBegan:touches withEvent:event];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.dragging) {
        NSLog(@"Moved ==>");
        [self.nextResponder touchesMoved:touches withEvent:event];
    }
    else {
        [super touchesMoved:touches withEvent:event];
    }

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.dragging) {
        NSLog(@"Ended ==>");
        [self.nextResponder touchesEnded:touches withEvent:event];
    }
    else {
        [super touchesEnded:touches withEvent:event];
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.dragging) {
        NSLog(@"Cancelled ==>");
        [self.nextResponder touchesCancelled:touches withEvent:event];
    }
    else {
        [super touchesCancelled:touches withEvent:event];
    }
}

sometimes it doesn't work in some cases. For example, when the UICollectionView is loaded first time, didSelectItemAtIndexPath method can be called when you tap, but after some scrolls or taps, the method is not called anymore, even if you try to reload the UICollectionView, it's still not working.

I try to log the message in the touch methods of UICollectionViewCell, the touches & event is forwarded. But why the didSelectItemAtIndexPath of UICollectionView is called if the cell get the gesture?

Any advice would be much appreciated.

UPDATE: 1.the UICollectionViewCell is loaded from nib file.

chancyWu
  • 14,073
  • 11
  • 62
  • 81

3 Answers3

0

It's not a good idea to use nextResponder if you're targeting a specific object. As you've just witnessed, the responder chain can change and you may not always get the object you expect. Why this is happening here can be any number of reasons, but you should use a more reliable means in this situation anyways.

Since UIScrollView is a subview of the UICollectionViewCell you can just pass the event directly to your superview [self.superview touchesMoved:touched withEvent:event]; You need to keep view hierarchy in mind when you're doing this and may have to call the superview's superview to reach the cell.

Also I wouldn't recommend using touches to intercept user interactions. I would highly recommend using UIGestureRecognizers as that will provide better more consistent functionality.

Literphor
  • 498
  • 4
  • 16
  • I try to use `self.superview`. it's also not reliable. Actually the gesture is forwarded to the cell. but the didselect method is not called. quite weird. – chancyWu Jun 19 '14 at 08:30
0

You should always call the super methods.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    NSLog(@"Began ==>");
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    NSLog(@"Moved ==>");
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    NSLog(@"Ended ==>");
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"Cancelled ==>");
}
odm
  • 888
  • 1
  • 14
  • 22
  • Thanks for that. I mean, I knew it, but you made me check, and I was missing the `touchesCancelled` super call, which killed my touch handling randomly after scrolling around a bit. Presumably a touch was in a half-handled state. – Echelon Nov 18 '15 at 16:23
  • Not true "if you override this method without calling super (a common use pattern), you must also override the other methods for handling touch events, if only as stub (empty) implementations." https://developer.apple.com/documentation/uikit/uiresponder/1621116-touchescancelled?language=objc – Colin Swelin Jan 14 '19 at 16:50
  • Not sure: "If you override this method without calling super (a common use pattern), you must also override the other methods for handling touch events, if only as stub (empty) implementations." https://developer.apple.com/documentation/uikit/uiresponder/1621116-touchescancelled?language=objc – Colin Swelin Jan 14 '19 at 16:50
  • Not sure, https://developer.apple.com/documentation/uikit/uiresponder/1621116-touchescancelled?language=objc – Colin Swelin Jan 14 '19 at 16:51
  • Not true, if you don't call super one of them you must override the remaining and do the same (from apple docs). – Colin Swelin Jan 14 '19 at 16:52
0

Actually it's because of self.dragging understanding. The correct way to forward the tap gesture is as below:

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

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

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

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.nextResponder touchesCancelled:touches withEvent:event];
}
chancyWu
  • 14,073
  • 11
  • 62
  • 81