iOS 11 brought a new way to add custom swipe swipe actions to UITableViewCell
's via the new UISwipeActionsConfiguration
class and associated UITableViewDelegate
methods.
When adding swipe actions you can add either leading swipe actions, trailing swipe actions or both kinds of swipe actions. The keys to adding swipe actions start with new UITableView
delegate methods, defined as follows:
func tableView(UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? func tableView(UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
As you can see in the method signatures, these both return a UISwipeActionsConfiguration
class. This class is initialized with an array of UIContextualAction
classes.
For this tutorial you will need to setup a UITableView
. If you don't have one setup you can take a look at this tutorial.
Let’s take a deeper dive into the trailing actions method, since it will apply more than one possible swipe action.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let deleteAction = self.contextualDeleteAction(forRowAtIndexPath: indexPath) let flagAction = self.contextualFlagAction(forRowAtIndexPath: indexPath) let swipeConfig = UISwipeActionsConfiguration(actions: [deleteAction, flagAction]) return swipeConfig }
Here, we are using a helper method to instantiate each of the UIContextualAction
classes we will need to build our swipe actions. Then we can instantiate a UISwipeActionsConfiguration
using the UIContextualAction
classes to populate the actions array. The UISwipeActionsConfiguration
is then returned from the delegate method.
If you do not want an action to run with a full swipe, you can add the following line of code just below the where we created the configuration:
configuration.performsFirstActionWithFullSwipe = false
This will prevent the first action from running when someone does a full swipe.
Next, lets take a look at the UIContextualAction
. This is where the work happens. The initializer takes a UIContextualAction.Style
, title
, and UIContextualActionHandler
block. The UIContextualActionHandler
gives you access to the UIContextualAction
, the view that displayed the action, and a completion handler that you pass a bool indicating whether or not the action was successful.
func contextualFlagAction(forRowAtIndexPath indexPath: IndexPath) -> UIContextualAction { let item = items[indexPath.row] let lastChar = Int(String(item.last!)) ?? 0 let less5 = lastChar < 5 let action = UIContextualAction(style: .normal, title: "Flag") { (action, view, completion) in if less5 { self.items[indexPath.row] = "\(item) + " self.tbl.cellForRow(at: indexPath)?.accessoryType = .checkmark self.tbl.reloadRows(at: [indexPath], with: .none) completion(true) } else { completion(false) } } action.image = UIImage(named: "flag") action.backgroundColor = less5 ? UIColor.gray : UIColor.orange return action } func contextualDeleteAction(forRowAtIndexPath indexPath: IndexPath) -> UIContextualAction { let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completion) in self.items.remove(at: indexPath.row) self.tbl.deleteRows(at: [indexPath], with: .automatic) //self.updateHeaderNumber(to: self.notifications.count) completion(true) } action.backgroundColor = .systemRed action.image = UIImage(named: "delete") return action }