問題描述
刪除 tableview 單元格不會更新索引 (Deleting tableview cells won't update index)
我有一個具有多個刪除功能的 UITableview。當用戶選擇一個單元格時,即;第 5 行。它刪除該行以及之前的每一行 (0...5)。雖然理論上這可以正常工作。在 beginUpdates
& 中調用 tableView.deleteRows(at:)
時行的索引路徑不會更新 endUpdates
.
一種解決方法涉及調用 reloadSections
或 reloadData
但這會產生不需要的行為,因為它會過早地結束 deleteRows
動畫。和/或延遲後這樣做。(即如下)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
tableView.reloadSections(IndexSet(integer: 0), with: .none)
}
tableView.deselectRow(at: indexPath, animated: true)
tableView.beginUpdates()
var allIndexPaths = [IndexPath]()
for index in 0...indexPath.row {
allIndexPaths.append(.init(row: index, section: 0))
}
self.tableData.removeSubrange(0...indexPath.row)
tableView.deleteRows(at: allIndexPaths, with: .fade)
tableView.endUpdates()
我創建了一個測試應用來顯示這個問題。
向下滾動,然後再次向上修復此問題。所以這顯然是一個出隊問題。
參考解法
方法 1:
Your code works as expected. The "row 0" text is just that; text in a label. There is no dynamic binding between the row index and that label's text.
The only way you are going to see the row numbers update is if you explicitly reload the visible rows.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let allIndexPaths = (0...indexPath.row).map { IndexPath(row:$0, section:0) }
self.tableData.removeFirst(indexPath.row+1)
tableView.deleteRows(at: allIndexPaths, with: .fade)
tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows ?? [], with: .none)
}
Generally you wouldn't expose the row numbers to the user; they are just an implementation detail.
方法 2:
Actually what you are expecting is exactly the opposite of what you should expect. You've misapprehended the purpose of batch updates. The idea here is that the user is deleting the cell and its corresponding data. The table view is just a presentation of the data.
Moreover, cells are reused. So they need to be kept tied to the data they represent. Cells do not represent the row they happen to be in at that moment; they represent the data corresponding to that row. When cells are deleted, the corresponding data is deleted, and so the correspondence keeps working between all the rest of the cells and the data.
To illustrate, forget about rows for a moment and think only about numbers. It may be that the cell in row 0 was associated with the data "0" to start with; but if the user deletes that row, what should happen is that that cell and the "0" are gone! That is what the user was trying to do: by way of the table, the user removes the data, which in this case is the "0".
So now there is no "0" in the data. And the "0" must not magically return from the dead. But that is exactly what you try to make it do. And so the results are incoherent.
The error in your code, then, is that after the deletion you then proceed to make the data rise from the dead, by trying to reassociate the cells with the row number they happen to occupy now. No. The cell needs to be associated consistently with a piece of data.
So, in your resetData
, change the key line populating the cell to this:
textLabel?.text = "Piece of data: \(data.index)"
Suddenly the correct and expected behavior of the table view will spring to life — simply because you have let go of the misapprehension that a cell represents the index number of the row it happens to be in at the moment. It doesn't. It represents the data at that index number.
Data is user‑facing. Index numbers are not. And now you see what trouble you can get into by trying to make index number be user‑facing! Distinguish between model and view and all will be well. The view shows the user the model — it does not show the user facts about itself. View is not model; data is not stored in the interface, but in some sort of internal data storage. That, in fact, is exactly why table views have a data source.
方法 3:
Use performBatchUpdates
instead.
beginUpdates/endUpdates sometimes doesn't work somehow.
‑‑‑Update‑‑‑
I ran the project you provided on Github. It worked well, I didn't see the problems you mentioned. So I can only guess, this happened base on the devices, especially for the simulators.
Try to put these codes bellow endUpdates
to refresh the UI of tableview:
tableView.setNeedsUpdateConstraints()
tableView.needsUpdateConstraints()
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
tableView.superview.setNeedsLayout()
tableView.superview.layoutIfNeeded()
(by Harry J、Paulw11、matt、irons163)