rushelmet
rushelmet

Reputation: 305

GestureRecognizer only works on one cell

I am trying to get my cells swipeable with a gestureRecognizer :

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell Identifier";

    self.cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
    // Fetch Bookmark
    NSDictionary *bookmark = [self.bookmarks objectAtIndex:indexPath.row];

    // Configure Cell
    self.cell.textLabel.numberOfLines = 0;
    self.cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.cell.textLabel.text = [bookmark objectForKey:@"name"];
    self.cell.detailTextLabel.text = [bookmark objectForKey:@"url"];

    //Swipe recognizer
    UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [self.cell addGestureRecognizer:recognizer];

    return self.cell;
}

And I have those methods who are supposed to make my cells moving but also going back to their initial position when I release the touch gesture :

-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
    CGPoint translation = [gestureRecognizer translationInView:self.cell];
    // Check for horizontal gesture
    if (fabsf(translation.x) > fabsf(translation.y)) {
        return YES;
    }
    return NO;
}

-(void)handlePan:(UIPanGestureRecognizer *)recognizer {
    // 1
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        // if the gesture has just started, record the current centre location
        _originalCenter = self.cell.center;
    }

    // 2
    if (recognizer.state == UIGestureRecognizerStateChanged) {
        // translate the center
        CGPoint translation = [recognizer translationInView:self.cell];
        self.cell.center = CGPointMake(_originalCenter.x + translation.x, _originalCenter.y);
        // determine whether the item has been dragged far enough to initiate a delete / complete
        _deleteOnDragRelease = self.cell.frame.origin.x < -self.cell.frame.size.width / 2;

    }

    // 3
    if (recognizer.state == UIGestureRecognizerStateEnded) {
        // the frame this cell would have had before being dragged
        CGRect originalFrame = CGRectMake(0, self.cell.frame.origin.y,
                                          self.cell.bounds.size.width, self.cell.bounds.size.height);
        if (!_deleteOnDragRelease) {
            // if the item is not being deleted, snap back to the original location
            [UIView animateWithDuration:0.2
                             animations:^{
                                 self.cell.frame = originalFrame;
                             }
             ];
        }
    }
}

The gesture itself works correctly but I can only swipe the last cell and when I scroll my UITableView it becomes the first cell that I can swipe.

Did you see from where the mistake could come ?

Upvotes: 1

Views: 1005

Answers (2)

Mostafa Sultan
Mostafa Sultan

Reputation: 2438

        BOOL addGesture = true;
        for (int i = 0;i < cell.gestureRecognizers.count;i ++) {
            id gest = cell.gestureRecognizers[i];
            if ([gest isKindOfClass:[UIPanGestureRecognizer class]]) {
                addGesture = false;
                break;
            }

        }
        if (addGesture) {
            UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
            [cell.userImageView addGestureRecognizer: panRecognizer];
        }

so that we don't depend on apple cell structure changes to be safe :)

Upvotes: 0

Duncan C
Duncan C

Reputation: 131426

I see a number of problems with your code.

The first is that you are using a property, self.cell, to create your gesture recognizer, and also in your handlePan: method.

Think about that for a minute. If you try to pan from the 3rd cell in your table view, why would self.cell magically contain the correct cell? Answer, it won't.

You need to convert your code to figure out which cell is being tapped based on info from the gesture recognizer. A gesture recognizer has a view associated with it that is the view that triggered the gesture. Since you attached your gesture recognizer directly to the cell, the gesture recognizer's view property should be the cell that the user is dragging. (You might need to attach the gesture recognizer to cell.contentView instead of cell.view. That's what I've always done. But if attaching it directly to the cell works, fine. That makes it simpler.)

Next problem: Your code is adding a gesture recognizer to a cell every time cellForRowAtIndexPath is called. If a cell gets recycled, it will already have a gesture recognizer attached to it, so you will then have 2 gesture recognizers attached to the cell. Then if have a long table view and scroll more, when it gets recycled again, it will have 3. Then 4, and so on. Bad.

You need some way to only add a gesture recognizer to a cell if it doesn't have one already. One way to do that is to create a custom subclass of UITableViewCell, and attach a gesture recognizer to the cell directly in the XIB file for that cell, (or in the init method for you custom cell). This is the approach I would use.

Another way to do it that would involve less changes to your code: After dequeuing a cell, check cell.gestureRecognizers.count. If it's zero, add your gesture recognizer. Otherwise it already has one. (Note. I'm not positive if the system attaches one or more gesture recognizers to the cell. If so, you might need to modify your logic to figure out if the cell has already had one of your gesture recognizers attached to it.)

Upvotes: 1

Related Questions