Currently, I have a screen that shows a TableView with custom cells that each have one text field and one button. I am storing the text field's data inputs into taskName dict and the button's title selection (from a pop-up screen) into taskTime dict.
However, my issue is when I swipe-to-delete a row in the TableView, it deletes the data stored in the row, but doesn't delete the row itself (as seen in the animation below).
Here is my code:
Screen with TableView + custom cells
class TaskListViewController: UIViewController {
@IBOutlet weak var taskList: SelfSizedTableView!
var taskCount: Int = 1
var taskName = [Int:String]()
var taskTime = [Int:String]()
override func viewDidLoad() {
super.viewDidLoad()
// Set initial taskTime value
taskTime[0] = "Set time"
taskList.delegate = self
taskList.dataSource = self
}
}
extension TaskListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return taskCount
}
// Return custom cell + data to insert in table view
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskCell
cell.delegate = self
// Configure nameField and timeButton in taskCell
cell.nameField.text = taskName[indexPath.row]
cell.timeButton.setTitle(taskTime[indexPath.row], for: .normal)
return cell
}
func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath) {
// Update # of rows in taskList
taskCount -= 1
// Row that is deleted
let deleteRowIndex = indexPath.row
print(deleteRowIndex)
// Remove taskName + taskTime from dictionary
taskName[deleteRowIndex] = nil
taskTime[deleteRowIndex] = nil
// Delete row from table view
let indexPaths = [indexPath]
taskList.deleteRows(at: indexPaths, with: .fade)
// Reload table view with new data
taskList.reloadData()
}
}
First of all don't use two dictionaries as data source use one array of a custom struct
struct Task {
var name, time : String
}
var tasks = [Task]()
override func viewDidLoad() {
super.viewDidLoad()
// Set initial taskTime value
tasks.append(Task(name:"", time: "Set time"))
taskList.delegate = self
taskList.dataSource = self
}
And don't hard-code the number of cells, count
the array, taskCount
is not needed.
extension TaskListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
// Return custom cell + data to insert in table view
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskCell
cell.delegate = self
// Configure nameField and timeButton in taskCell
let task = tasks(indexPath.row)
cell.nameField.text = task.name
cell.timeButton.setTitle(task.time, for: .normal)
return cell
}
And it's highly recommended to use tableView(_:trailingSwipeActionsConfigurationForRowAt:
rather than tableView(_:commit:forRowAt
In this method remove the item at the given index path and call deleteRows(at:with:)
. Calling reloadData()
right after deleteRows(at:with:)
is redundant. The latter methods updates the UI
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: "Delete") { [unowned self] action, view, completionHandler in
self.tasks.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
completionHandler(true)
}
return UISwipeActionsConfiguration(actions: [delete])
}
}
Of course you have to refactor the other occurrences of the two data source dictionaries to match the data source array.