[Tips]didEndEditingRowAtを扱う際の注意点


UITableViewDelegateのtableView(_:didEndEditingRowAt:)は、セルのスワイプやEditModeを扱う際に触る機会のあるDelegateのAPIですが、注意点が1つあります。

それはスワイプアクションによってセルが削除されても呼ばれる可能性があるという事です。

スワイプによる削除は以下の2種類あると認識しています。

  • tableView(_:commit:forRowAt:)を実装しつつ、tableView(_:trailingSwipeActionsConfigurationForRowAt:)nilで返している時の標準スワイプ削除
  • tableView(_:trailingSwipeActionsConfigurationForRowAt:)で削除アクションを含むConfigurationを返している時の独自スワイプ削除

もしかしたら標準スワイプ削除を利用している場合は、内部の挙動によってtableView(_:didEndEditingRowAt:)が呼ばれないことがあるのかもしれませんが、後者の実装をしている場合は呼ばれることがあります。

そのため、以下のようなコードを書いているとクラッシュします。


func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {

    let indexPath = indexPath!
    let cell = tableView.cellForRow(at: indexPath)! // クラッシュ!

    // cellに対するアクション
}

独自スワイプ削除でセルを削除した場合、indexPath自体はnilではありませんが、tableView.cellForRow(at: indexPath)の結果がnilになりクラッシュします。

また、API設計理由を考えても何かしらの理由でindexPath自体がnilで渡されるケースが存在するはずのため、indexPathのケアも必要です。

このような意図しないクラッシュを避けるために、以下のようにOptional-Bindingを正しく利用して記述しましょう。


func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {

    // もしくはguard-else節など
    if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
        // cellに対するアクション
    }

}