Reputation: 305
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
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
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