Table Swipe Actions

Adding edit actions to table view rows has been possible since iOS 8 but only on the right (trailing) side. The new style swipe actions added in iOS 11 and used by Apple in Mail and other apps can be on either side and can include images. Here is all you need to know about adding swipe actions to a table view.

See also SwiftUI Swipe Actions.

Edit Actions (iOS 8)

We have had edit actions since iOS 8 as a way to display custom actions when the user swipes a table row.

Table view edit actions

You add edit actions in the table view delegate with the tableView(_:editActionsForRowAt:) method:

override func tableView(_ tableView: UITableView,
  editActionsForRowAt indexPath: IndexPath)
  -> [UITableViewRowAction]? {

  let deleteTitle = NSLocalizedString("Delete", comment: "Delete action")
  let deleteAction = UITableViewRowAction(style: .destructive,
    title: deleteTitle) { (action, indexPath) in
    self.dataSource?.delete(at: indexPath)
  }

  let favoriteTitle = NSLocalizedString("Favorite", comment: "Favorite action")
  let favoriteAction = UITableViewRowAction(style: .normal,
    title: favoriteTitle) { (action, indexPath) in
    self.dataSource?.setFavorite(true, at: indexPath)
  }
  favoriteAction.backgroundColor = .green
  return [favoriteAction, deleteAction]
}

You return an array of UITableViewRowAction items. The first item is the outermost action. Each action has a (localized) title, a style and a handler block for when the user taps the action. The background color is red for .destructive style actions (also for the .default style) or gray for .normal style actions but you can customize it.

The limitation of edit actions is that they work only on the trailing edge. There is also no way to add an image.

Swipe Actions (iOS 11)

Swipe actions are new in iOS 11 and are a replacement for edit actions. If you are already familiar with edit actions you will find swipe actions familiar. The big change is that they can be on the leading or trailing sides and include an image:

Leading swipe action

The table view delegate method to add swipe actions is similar to edit actions. In this example I add a leading action that allows you to favorite or unfavorite an item (there is also a trailingSwipeActionsConfigurationForRowAtIndexPath version):

override func tableView(_ tableView: UITableView,
  leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
  ->   UISwipeActionsConfiguration? {

  // Get current state from data source
  guard let favorite = dataSource?.favorite(at: indexPath) else {
    return nil
  }

  let title = favorite ?
    NSLocalizedString("Unfavorite", comment: "Unfavorite") :
    NSLocalizedString("Favorite", comment: "Favorite")

  let action = UIContextualAction(style: .normal, title: title,
    handler: { (action, view, completionHandler) in
    // Update data source when user taps action
    self.dataSource?.setFavorite(!favorite, at: indexPath)
    completionHandler(true)
  })

  action.image = UIImage(named: "heart")
  action.backgroundColor = favorite ? .red : .green
  let configuration = UISwipeActionsConfiguration(actions: [action])
  return configuration
}

You return a UISwipeActionsConfiguration which has an array of UIContextualAction items. Each swipe action, like a UITableViewRowAction, has a title, style (.normal or .destructive) and handler closure. The .destructive style animates the removal of the cell.

The closure is called when the user taps the action and has action, source view and completion handler arguments. You call the completion handler with true to indicate you performed the action or false if you could not for some reason.

I cannot find any specific guidelines for the image but following the advice for custom icons is probably a good idea. The examples in this post are 36 x 36 point template images.

If the row is high enough it shows both the image and title:

Action image and title

By default a full swipe across the row performs the first action. Change the configuration to disable that:

configuration.performsFirstActionWithFullSwipe = false

If you only add leading swipe actions and don’t want the default delete action on the trailing side disable the default editing style for the table:

override func tableView(_ tableView: UITableView,
  editingStyleForRowAt indexPath: IndexPath)
  -> UITableViewCellEditingStyle {
  return .none
}

Which Should You Use?

Swipe actions replace the older edit actions and are the preferred way starting with iOS 11. The edit action API is not deprecated in iOS 11 but the table view header comments indicate it will be in a future release:

Use -tableView:trailingSwipeActionsConfigurationForRowAtIndexPath: instead of this method, which will be deprecated in a future release.

This reminds me of the situation with property animators replacing the not yet deprecated block-based view animations.

Further Details

WWDC 2017 briefly covered the introduction of table swipe actions in two sessions: