Full swipe UITableViewCell to delete UITableView iOS 8
Asked Answered
R

4

18

I'd like to mimic the swipe to delete function of a UITableViewCell just like the mail app in iOS 8. I'm not referring to swipe to reveal a delete button. I'm referring to when you swipe, it discoloses 3 actions, but if you keep swiping to the left, the email is deleted.

In iOS 8, UITableView has a new method where you can provide the data to display any number of buttons:

#ifdef __IPHONE_8_0
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewRowAction *viewStackRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Stack" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"View Stack Action");
    }];
    viewStackRowAction.backgroundColor = [UIColor radiusBlueColor];

    UITableViewRowAction *viewUserRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"User" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"View User Action");
    }];
    viewUserRowAction.backgroundColor = [UIColor radiusLightBlueColor];

    UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Delete" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"Delete");
    }];
    deleteRowAction.backgroundColor = [UIColor redColor];


    return @[deleteRowAction, viewUserRowAction, viewStackRowAction];
}
#endif

I don't see any API to detect if you keep swiping though. I've grepped for 8_0 in UITableView.h and the above method seems to be the only new one.

I suppose one could monitor the scroll view offset, or add/hijack a UIPanGestureRecognizer. I just wanted to make sure to use the default way, if there is one (and get the animation for "free")

Relativity answered 23/9, 2014 at 19:0 Comment(0)
D
6

With Swift 4.2 and iOS 12, according to your needs, you can choose one of the 3 following ways in order to create a trailing swipe action that will delete the selected UITableViewCell.


#1. Using UITableViewDataSource's tableView(_:commit:forRowAt:)

When you use tableView(_:commit:forRowAt:) with an editingStyle of value UITableViewCell.EditingStyle.delete, full swipe to delete is automatically supported by the system.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if (editingStyle == UITableViewCell.EditingStyle.delete) {
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }

}

#2. Using UITableViewDelegate's tableView(_:editActionsForRowAt:) and UITableViewRowAction

In order to support full swipe to delete with UITableViewRowAction, you have to initialize it with a style that has a value of UITableViewRowAction.Style.destructive.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        // Intentionally blank in order to be able to use UITableViewRowActions
    }

    override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let deleteHandler: (UITableViewRowAction, IndexPath) -> Void = { _, indexPath in
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
        let deleteAction = UITableViewRowAction(style: UITableViewRowAction.Style.destructive, title: "Delete", handler: deleteHandler)
        // Add more actions here if required
        return [deleteAction]
    }

}

#3. Using UITableViewDelegate's tableView(_:trailingSwipeActionsConfigurationForRowAt:) and UISwipeActionsConfiguration (requires iOS 11)

UISwipeActionsConfiguration has a property called performsFirstActionWithFullSwipe. performsFirstActionWithFullSwipe has the following declaration:

var performsFirstActionWithFullSwipe: Bool { get set }

A Boolean value indicating whether a full swipe automatically performs the first action. [...] When this property is set to true, a full swipe in the row performs the first action listed in the actions property. The default value of this property is true.

The following UITableViewController implementation show how to use UISwipeActionsConfiguration in order to manage full swipe to delete actions.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let handler: UIContextualAction.Handler = { (action: UIContextualAction, view: UIView, completionHandler: ((Bool) -> Void)) in
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            completionHandler(true)
        }
        let deleteAction = UIContextualAction(style: UIContextualAction.Style.destructive, title: "Delete", handler: handler)
        // Add more actions here if required
        let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
        configuration.performsFirstActionWithFullSwipe = true
        return configuration
    }

}
Danby answered 10/9, 2017 at 22:48 Comment(1)
Yes, I'm happy to see that they have added this.Relativity
J
2

add ui gustere recognizer to each cell, check the amount of "swipness", if its above specific threshold, do the deletion.

somthing like:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"identifier";

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]];
        }

        UISwipeGestureRecognizer* swipe_gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeft:)];
        [swipe_gesture setDirection:UISwipeGestureRecognizerDirectionLeft];
        [cell addGestureRecognizer:swipe_gesture];


        return cell;
    }

- (void)swipeLeft:(UIGestureRecognizer *)gestureRecognizer {
    int threshold = 100;
    if (sender.state == UIGestureRecognizerStateBegan) 
    {
        startLocation = [sender locationInView:self.view];
    }
    else if (sender.state == UIGestureRecognizerStateEnded) 
    {
        CGPoint stopLocation = [sender locationInView:self.view];
        CGFloat dx = stopLocation.x - startLocation.x;
        CGFloat dy = stopLocation.y - startLocation.y;
        CGFloat distance = sqrt(dx*dx + dy*dy );
        if (distance > threshold )
        {
            NSLog(@"DELETE_ROW");
        }

    }
}
Jankowski answered 21/10, 2014 at 15:37 Comment(1)
Hello. It would be better if you're more specific and explain more. In the swipeLeft method you are using sender and startLocation. Where do they come from? and where do you add these methods? ThanksSlipper
V
0

Your table view's data source has to implement

-tableView:commitEditingStyle:forRowAtIndexPath:

otherwise the built-in iOS 8 swiping functionality will not work.

This seems counterintuitive since a UITableViewRowAction accepts a block. But it's the only way I've been able to get it to work.

Viniferous answered 11/12, 2014 at 3:3 Comment(1)
This does not give the capability to delete the table cell with a full swipe. It enables displaying the delete button, user still need to touch the button to delete.Pauly
C
0

You can use MGSwipeTableCell. They have implement this feature to fire callback swipeTableCell:tappedButtonAtIndex:direction:fromExpansion: with tappedButtonAtIndex equal to 0 (so it gets executed what you implemented on first added button).

Cherri answered 19/9, 2016 at 9:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.