iOS UITableViewのdeleteRows(at:with:)で落ちる
English version is below.
はじめに
UITableViewを使っていて、deleteRow(at:with:) を読んだときにクラッシュする問題にぶつかりました。
その解決方法について紹介します。
問題
次のようなコードを書いたときにアプリがクラッシュしました。
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { guard editingStyle == .delete else { return } self.tableView.deleteRows(at: [indexPath], with: .fade) // クラッシュ // myArrayはデータソースです。 self.myArray.remove(at: indexPath.row)
エラーメッセージは次の通り。
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.54.4/UITableView.m:2012
解決方法
UITableViewのdeleteRow(at:with:)はデータソースを変更する前に読んではいけません。 したがって、次のようなコードが正解です。
なので、たまにデータソースの削除に失敗することがある場合、注意が必要です。 何かしらの方法で削除が失敗したことを通知する必要があります。
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { guard editingStyle == .delete else { return } // myArrayはデータソースです。 self.myArray.remove(at: indexPath.row) self.tableView.deleteRows(at: [indexPath], with: .fade) // データソース変更後にUITableViewを削除する操作
その他
UITableViewのdeleteRow(at:with:)を呼ぶときに、beginUpdates() と endUpdates()を呼ぶ必要がありそうです。
しかし今回の場合では必要ありません。
公式ドキュメントによると、これらのメソッドはアニメーションブロック内でdeleteRow(at:with:)を呼ぶときに必要なようです。
参考URL
https://developer.apple.com/documentation/uikit/uitableview/1614960-deleterows