post Image
TableViewで複数セルを一気に複数削除する

はじめに

TableView上のセルの削除を行うとき、canEditRowAtIndexPathcommitEditingStyle等のdelegateメソッドを駆使することで、右スワイプでの削除や編集ボタンをトリガーにセルを削除したりすることができます。
ただ、これらはいずれも「対象の一つのセルしか一度に削除できない」という制約に阻まれます。
右スワイプはもちろん、せっかく編集モードになっているのに削除できるセルは1つずつです。
そこで、複数のセルを1セクションで選択でき、それをそのまま一気に消せるUIを作ってみたので、作るにあたってのTipsをまとめます。

つくったものの動き

input.gif

Tips

ここからは複数削除において気をつけなければいけないことを紹介していきますが、
前提として
– TableViewへの値の設置
– NavigationController、およびそのボタンの設置
等の基本的な説明は省きます。

Tips1 TableViewの編集モードで複数選択を許可する

まず、TableViewの編集モードで複数選択を許可してあげる必要があります。
そのときはTableViewのプロパティであるallowsMultipleSelectionDuringEditingをtrueにしてあげればOKです。

allowsMultipleSelectionDuringEditing
tableView.allowsMultipleSelectionDuringEditing = true

こうすることで、TableViewが編集モードになると複数選択できるViewとして表示されます。

名称未設定.001.jpeg

Tips2 選択肢情報の格納される順番について

複数選択モードで何かのセルを選択状態にしたとき、そのイベントはdidSelectRowAtで取得することができます。
逆に選択状態を解除したときは、didDeselectRowAtで取得することが出来ます。
また、「どの行が選択されているか」という情報は、TableViewのindexPathsForSelectedRowsというプロパティに[IndexPath]の型で格納されます。
このindexPathsForSelectedRowsに格納される順番が少し曲者で、上から順番に格納されるのではなく「セルがタップされた順番」に格納されます。

111111.001.jpeg

たとえば上図の順番で選択されたとすると、indexPathsForSelectedRowsの値は[[0,3],[0,7],[0,6],[0,1]]となります。
※ 0はセルが所属するセクション番号を表します。今回は1セクションでのTableViewなので常に0ですね。
これの何が曲者かというのは次のTips3で紹介します。

Tips3 cellに表示する情報の削除方法

ここまでで選択肢情報が取得できたので、あとは削除するだけです。
ここで削除するのは
1. TableViewのセルそのものの削除
2. TableViewのセルに表示する情報の削除

の2つが必要になります。

  1. TableViewのセルそのものの削除
    これは簡単です。
    TableViewはdeleteRowsメソッドを持っているので、取得した選択肢情報を引数に渡してあげれば削除できます。
deleteRows
tableView.deleteRows(at: tableView.indexPathsForSelectedRows, with: UITableViewRowAnimation.automatic)
  1. TableViewのセルに表示する情報の削除
    こっちが少し厄介です。
    たとえば削除対象の情報を配列で持っているケースの場合、for文を使ってremoveで要素を順番に削除することが考えられますが、
    indexPathsForSelectedRowsをそのまま渡して削除しようとすると、チェックした順番でindexPathが格納されているため、removeする度に削除対象の要素番号がズレてしまいます。
indexPathsForSelectedRowsの順番のまま削除するとダメ
for indexPathList in indexPathsForSelectedRows {
    deleteArray.remove(at: indexPathList.row)
}

なのでこの場合はindexPathsForSelectedRowsを降順にソートしてからfor文で削除します。
なぜ昇順ではないかというと、これも要素番号がズレるからです。
たとえば昇順にソートした結果、indexPathsForSelectedRows[[0,1],[0,2],[0,4],[0,5]]になった場合、これらの要素をfor文で削除しようとすると、
・最初に[0,1]が削除される → OK
・[0,1]が削除されたことによって、それより後ろの要素が前に詰まる。
([0,2]→[0,1]、[0,4]→[0,3]、[0,5]→[0,4になる])
・次に削除すべき[0,2]の要素は[0,1]に移動しているため、削除対象が異なってしまう
となってしまいます。
降順になっていると、このようなindexのズレが起こることはなくなります。

indexPathsForSelectedRowsを降順にソートしてから削除
let sortedIndexPaths = indexPathsForSelectedRows.sorted { $0.row > $1.row }
for indexPathList in sortedIndexPaths {
    deleteArray.remove(at: indexPathList.row)
}

まとめ

以上がTableViewのセルを一気に複数削除する上でのTipsになります。
ソースコードはGithub上にあがっているので参考にしてみて下さい。


『 Swift 』Article List