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")
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
.
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)
}
}
}
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]
}
}
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 theactions
property. The default value of this property istrue
.
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
}
}