jyavenard
jyavenard

Reputation: 2095

6 different UISwipeGestureRecognizer, always receive the same one

I have a TableView containing UITableViewCells, each with 6 buttons...

A UISwipeGestureRecognizer is added to each of the buttons like so:

- (void)awakeFromNib {
    UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]
                                            initWithTarget:self
                                            action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_XXSButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_XSButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_SButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_MButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_LButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_XLButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
    rightSwipe = [[UISwipeGestureRecognizer alloc]
                  initWithTarget:self
                  action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [_XXLButton addGestureRecognizer:rightSwipe];
    [rightSwipe release];
}

- (void)handleRightSwipe:(UIGestureRecognizer *)gestureRecognizer {
    UIButton *button = (UIButton *)[gestureRecognizer view];
    [self zeroButton:button];
}

When the swipe is recognised, it calls the method zeroButton .

No matter which button I do the swipe on, the view retrieved is of the first button. I would have expected [gestureRecognizer view] to return the button the swipe was performed on. Any ideas why I always receive the gesture of the first button only?

I recognise that this code isn't the most elegant to start with, having to create 6 different UISwipeGestureRecognizer. I would have preferred to create the gesture on the UITableViewCell itself, and manually check if the swipe originated or passed over a particular button. Unfortunately, my attempt at this has proven unreliable. So if someone has a suggestion on how to implement that method instead would be even better (I'll post that "solution" later)

Thanks...

Upvotes: 1

Views: 2303

Answers (2)

jyavenard
jyavenard

Reputation: 2095

Actually, it was an error in the zeroButton code that caused it to always reset the first button. I used the tag assigned to the button to determine which action to create. My tags were incorrectly set! silly me. So the code in my original question was actually correct.

Since I have amended the code as follow:

- (void)awakeFromNib {
    UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]
                                            initWithTarget:self
                                            action:@selector(handleRightSwipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    rightSwipe.delegate = self;

    [self addGestureRecognizer:rightSwipe];
    [rightSwipe release];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[UIButton class]])
    {
        // only recognises gesture started on a button
        return YES;
    }
    return NO;
}

- (void)handleRightSwipe:(UIGestureRecognizer *)gestureRecognizer {
    // Determine which button received the right swipe

    // Get the position of the point tapped in the UIViewCell co-ordinate system
    CGPoint tapPoint = [gestureRecognizer locationInView:self];
    NSLog(@"%.1f %.1f", tapPoint.x , tapPoint.y);
    UIView *viewAtBottomOfHierarchy = [self hitTest:tapPoint withEvent:nil];
    if ([viewAtBottomOfHierarchy isKindOfClass:[UIButton class]])
    {
        [self zeroButton:(UIButton *)viewAtBottomOfHierarchy];
    }
}

This looks at where the swipe originate and determine which button it started into (if any)...

Upvotes: 1

sergio
sergio

Reputation: 69047

One alternative approach, since you are using UIButtons would be using addTarget:action:forControlEvents: on your buttons for the events:

UIControlEventTouchDragInside 
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter  
UIControlEventTouchDragExit  

and the do your handling in the action method you specify. It should not be difficult to "map" the branches within your gesture handling method to different methods (or just one method, if you like) inside of your class.

I understand this is not a completely satisfactory answer, since it gives up using gesture recognizers (and their facilities), but it could work for you.

As to why the gesture recognizer does not work correctly when applied to a UITableViewCell (and possibly it also explains why only the first button seems to receive the gesture), the behavior you described in your comment, reminded me that UIScrollView (base class of UITableView) is greedy with touches (not so much as a UIWebView, though) and that this for sure has an impact on how touches are forwarded. So, I don't know if it works, but I found this in UIScrollView:

delaysContentTouches

If the value of this property is YES, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is NO , the scroll view immediately calls touchesShouldBegin:withEvent:inContentView:. The default value is YES.

This could work or not, it could even make the UITableView not work at all, but it is easy to try out.

If this does not work and my previous hypothesis is still correct, a way around it would be subclassing UITableView and overriding the touch related methods (e.g., touchesShouldBegin:withEvent:inContentView:) so you can customize their behavior.

OLD and WRONG ANSWER:

It is my understanding the you can share one same gesture recognizer among different views, like this:

rightSwipe = [[UISwipeGestureRecognizer alloc]
              initWithTarget:self
              action:@selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_LButton addGestureRecognizer:rightSwipe];
[_SButton addGestureRecognizer:rightSwipe];
[_XButton addGestureRecognizer:rightSwipe];
[rightSwipe release];

I don't know if this can solve your issue, since what happens in your case is that, no matter which button you swipe, it is always that first gesture recognizer that gets activated and this is pretty hard to explain since you have different buttons. This could possibly be related to some overlapping (I am guessing) among the buttons, or to the fact that all of your buttons are "embedded" in a table cell, so the swipe mechanism at a cell level interferes with the swipe mechanism at the sub-cell level, but I am not sure about it.

As an alternative approach, which is surely working, you could attach your gesture recognizer to the very table cell (you mentioned your attempt to attach it to the table view itself; attaching to the table cell will work).

Hope this helps.

Upvotes: 1

Related Questions