I'm trying to implement an undo feature when deleting cells in a UITableView
. The cell data to be deleted is declared as a variable. The deletion part works fine, however when I attempt to 'undo' the deletion the variable is always empty.
Implementation
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: UITableViewRowAction.Style.destructive, title: "Delete") { (action, indexPath) in
let selectedExercise = self.exercises[indexPath.row]
//selectedExercise is declared here to be used in both
//`deleteExercise` and `undoDeleteExercise`
self.undoManager?.registerUndo(withTarget: self, handler: { (selfTarget) in
self.undoDeleteExercise(indexPath: indexPath, selectedExercise: selectedExercise)
})
self.deleteExercise(indexPath: indexPath, selectedExercise: selectedExercise)
let message = MDCSnackbarMessage()
message.text = "Removing Exercise"
let action = MDCSnackbarMessageAction()
action.handler = {() in
self.undoManager?.undo()
}
action.title = "UNDO"
message.action = action
MDCSnackbarManager.show(message)
}
return [delete]
}
Deleting
This works as expected. The selected exercise is removed from the database, exercises array and the tableView
.
func deleteExercise(indexPath: IndexPath, selectedExercise: Exercise){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let _ : NSError! = nil
do {
managedContext.delete(selectedExercise as NSManagedObject)
exercises.remove(at: indexPath.row)
self.execisesTableView.deleteRows(at: [indexPath], with: .automatic)
try managedContext.save()
} catch {
print("error : \(error)")
}
ifNoExercises()
}
Undoing a Deletion
The problem here is that selectedExercise
is always an empty object, so although the row is added into the database and the tableView
it does not contain any information.
func undoDeleteExercise(indexPath: IndexPath, selectedExercise: Exercise){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let _ : NSError! = nil
do {
managedContext.insert(selectedExercise as NSManagedObject)
exercises.insert(selectedExercise, at: indexPath.row)
execisesTableView.insertRows(at: [indexPath], with: .automatic)
try managedContext.save()
} catch {
print("error : \(error)")
}
ifNoExercises()
}
For anyone having the same problem this is how I fixed (this replaces editActionsForRowAt
):
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let contextItem = UIContextualAction(style: .destructive, title: "Delete") { (contextualAction, view, boolValue) in
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let exerciseEntity = NSEntityDescription.entity(forEntityName: MyVariables.exerciseEntity, in: managedContext)!
let copiedExercise = NSManagedObject(entity: exerciseEntity, insertInto: managedContext)
let selectedExercise = self.exercises[indexPath.row]
copiedExercise.setValue(selectedExercise.distance, forKeyPath: MyVariables.distance)
copiedExercise.setValue(selectedExercise.end_time, forKeyPath: MyVariables.endTime)
copiedExercise.setValue(selectedExercise.lat_lngs, forKeyPath: MyVariables.latLngs)
copiedExercise.setValue(selectedExercise.map_string, forKeyPath: MyVariables.mapString)
copiedExercise.setValue(selectedExercise.start_time, forKeyPath: MyVariables.startTime)
self.undoManager?.registerUndo(withTarget: self, handler: { (selfTarget) in
self.undoDeleteExercise(indexPath: indexPath, selectedExercise: copiedExercise as! Exercise)
})
self.deleteExercise(indexPath: indexPath, selectedExercise: selectedExercise)
let message = MDCSnackbarMessage()
message.text = "Removing Exercise"
let action = MDCSnackbarMessageAction()
action.handler = {() in
self.undoManager?.undo()
}
action.title = "UNDO"
message.action = action
MDCSnackbarManager.show(message)
}
let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])
return swipeActions
}