5

My UITableView has the swipe to delete feature enabled. Each cell has a UIButton on it that performs an action (in this case, perform a segue).

I'd expect that if I swipe the cell by touching the button, the button's action would be canceled/ignored, and only the swipe would be handled. What actually happens, however, is that both gestures (swipe + tap) are detected and handled.

This means that if I just want to delete one cell and "accidentally" swipe by touching the button, the app will go to the next screen.

How can I force my app to ignore the taps in this case?

Guilherme
  • 7,839
  • 9
  • 56
  • 99

4 Answers4

9

august's answer was nice enough for me, but I figured out how to make it even better:

Checking if the table was on edit mode to decide if the button should perform its action will make it behave as it should, but there will still be an issue in the user experience:

If the user wants to exit the editing mode, he should be able to tap anywhere in the cell to achieve that, including the button. However, the UIButton's action is still analyzed first by the app, and tapping the button will not exit editing mode.

The solution I found was to disable the button's user interaction while entering edit mode, and reenabling it when it's done:

// View with tag = 1 is the UIButton in question
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath {
  [(UIButton *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:1] setUserInteractionEnabled:NO];
}

- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {
  [(UIButton *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:1] setUserInteractionEnabled:YES];
}

This way, dragging the button to enter edit mode will not trigger the button's action, and taping it to exit edit mode will indeed exit edit mode.

Guilherme
  • 7,839
  • 9
  • 56
  • 99
2

One elegant way would be to ignore button taps as long as a cell has entered editing mode. This works because the swipe to delete gesture will cause willBeginEditingRowAtIndexPath to be called before the button tap action is invoked.

- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.isEditing = YES;
}

- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.isEditing = NO;
}

// button tapped
- (IBAction)tap:(id)sender
{
    if (self.isEditing) {
        NSLog(@"Ignore it");
    }
    else {
        NSLog(@"Tap");
        // perform segue
    }
}
augustzf
  • 2,385
  • 1
  • 16
  • 22
2

It's possible to do it also in your cell's subclass:

override func setEditing(editing: Bool, animated: Bool) {
    super.setEditing(editing, animated: animated)

    actionButton?.userInteractionEnabled = !editing
}
Anton Plebanovich
  • 1,296
  • 17
  • 17
0

Because my function being called from a button press was a delegate onto my main UITableViewController's class and connected to the UITableViewCell as an IBAction, the button in my UITableViewCell was still firing on a swipe that had the UIButton pressed as part of the swipe.

In order to stop that I used the same UITableView delegates as the accepted answer, but had to set a file level variable to monitor if editing was occurring.

// in custom UITableViewCell class
@IBAction func displayOptions(_ sender: Any) {
    delegate?.buttonPress()
}

// in UITableViewController class that implemented delegate
fileprivate var cellSwiped: Bool = false

func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
    cellSwiped = true
}

func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
    cellSwiped = false
}

func displayContactsSheet(for contact: Contact) {
    if cellSwiped {
        return
    }
    // proceed with delegate call button press actions
}
Hellojeffy
  • 1,851
  • 2
  • 19
  • 23