When I use trailingSwipeActionsConfigurationForRowAt
my TableView will show the delete and reorder options, however when selecting reorder
nothing happens. I think I have all of the correct methods and am calling setEditing
; is there anything else I'm missing? Thanks!
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
func setupTableView() {
tableView.frame = self.view.frame
tableView.dataSource = self
tableView.delegate = self
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
tableView.dragInteractionEnabled = true
self.view.addSubview(tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.backgroundColor = .gray
cell.showsReorderControl = true
return cell
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.tableView.setEditing(editing, animated: animated)
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .normal, title: "delete") { (action, view, completion) in
tableView.reloadData()
completion(true)
}
let reorderAction = UIContextualAction(style: .normal, title: "reorder") { (action, view, completion) in
tableView.setEditing(true, animated: true)
completion(true)
}
return UISwipeActionsConfiguration(actions: [deleteAction, reorderAction])
}
}
class CustomCell: UITableViewCell {
}
Result after swiping:
After selecting reorder
:
CodePudding user response:
A few observations:
You are not going to get the reorder controls if you do not implement
tableView(_:moveRowAt:to:)
, e.g., assuming you had a model which was an array calledobjects
, you could do the following:func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { let object = objects.remove(at: sourceIndexPath.row) objects.insert(object, at: destinationIndexPath.row) }
The
trailingSwipeActionsConfigurationForRowAt
is probably not the right place to put a “reorder” command. Part of the reason is that once the table view is in edit mode and you tap on the ⛔️, the trailing actions show up, and “reorder” does not make sense in that context. E.g., here I am tapping on ⛔️ and I see the confusing actions.I would suggest only adding “delete” as the trailing action. That way, you (a) get only “delete” if you tap on ⛔️ in
isEditing
mode, but also (b) get the stand-alone swipe action, too.You cannot initiate
isEditing
from the trailing swipe actions (and, as discussed above, I do not think you want to, anyway). So, if you do not have “reorder” in the trailing swipe actions, you need some other method to enter edit mode. E.g., above, I added an “edit” button to the navigation bar that togglesisEditing
:@IBAction func didTapEdit(_ sender: Any) { tableView.isEditing.toggle() }
Then, you can keep the swipe to delete functionality, but when you tap on edit button, you have the tap on ⛔️ to delete functionality (plus the handles for reordering because we added
tableView(_:moveRowAt:to:)
as outlined in step one, above):Another way to achieve reordering is to just allow drag and drop within the table view where you can long-press on a row and then drag it:
This is enabled by setting
dragInteractionEnabled
anddropDelegate
:class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! let formatter: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .spellOut return formatter }() private var objects: [Foo] = ... override func viewDidLoad() { super.viewDidLoad() ... tableView.dragInteractionEnabled = true tableView.dropDelegate = self } } // MARK: - UITableViewDataSource extension ViewController: UITableViewDataSource { ... } // MARK: - UITableViewDelegate extension ViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let deleteAction = UIContextualAction(style: .destructive, title: "delete") { [weak self] action, view, completion in self?.objects.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .middle) completion(true) } return UISwipeActionsConfiguration(actions: [deleteAction]) } // This is used if table view is in `isEditing` mode and by `UITableViewDropDelegate` func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { let object = objects.remove(at: sourceIndexPath.row) objects.insert(object, at: destinationIndexPath.row) } } // MARK: - UITableViewDropDelegate extension ViewController: UITableViewDropDelegate { func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal { guard session.items.count == 1, // Accept only one drag item ... tableView.hasActiveDrag // ... from within this table view else { return UITableViewDropProposal(operation: .cancel) } return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath) } func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) { guard let destinationIndexPath = coordinator.destinationIndexPath else { return } for item in coordinator.items { if let sourceIndexPath = item.sourceIndexPath { DispatchQueue.main.async { tableView.moveRow(at: sourceIndexPath, to: destinationIndexPath) } } } } }
Clearly, if you were going to enable drag from this app to others, you would add
UITableViewDragDelegate
conformance here, and make your model objects conform toNSItemProviderReading
andNSItemProviderWriting
. But the above should be sufficient for dragging and dropping to reorder within aUITableView
.