はじめに
TableView上のセルの削除を行うとき、canEditRowAtIndexPath
やcommitEditingStyle
等のdelegateメソッドを駆使することで、右スワイプでの削除や編集ボタンをトリガーにセルを削除したりすることができます。
ただ、これらはいずれも「対象の一つのセルしか一度に削除できない」という制約に阻まれます。
右スワイプはもちろん、せっかく編集モードになっているのに削除できるセルは1つずつです。
そこで、複数のセルを1セクションで選択でき、それをそのまま一気に消せるUIを作ってみたので、作るにあたってのTipsをまとめます。
つくったものの動き
Tips
ここからは複数削除において気をつけなければいけないことを紹介していきますが、
前提として
- TableViewへの値の設置
- NavigationController、およびそのボタンの設置
等の基本的な説明は省きます。
Tips1 TableViewの編集モードで複数選択を許可する
まず、TableViewの編集モードで複数選択を許可してあげる必要があります。
そのときはTableViewのプロパティであるallowsMultipleSelectionDuringEditing
をtrueにしてあげればOKです。
tableView.allowsMultipleSelectionDuringEditing = true
こうすることで、TableViewが編集モードになると複数選択できるViewとして表示されます。
Tips2 選択肢情報の格納される順番について
複数選択モードで何かのセルを選択状態にしたとき、そのイベントはdidSelectRowAt
で取得することができます。
逆に選択状態を解除したときは、didDeselectRowAt
で取得することが出来ます。
また、「どの行が選択されているか」という情報は、TableViewのindexPathsForSelectedRows
というプロパティに[IndexPath]の型で格納されます。
このindexPathsForSelectedRows
に格納される順番が少し曲者で、上から順番に格納されるのではなく「セルがタップされた順番」に格納されます。
たとえば上図の順番で選択されたとすると、indexPathsForSelectedRows
の値は[[0,3],[0,7],[0,6],[0,1]]
となります。
※ 0はセルが所属するセクション番号を表します。今回は1セクションでのTableViewなので常に0ですね。
これの何が曲者かというのは次のTips3で紹介します。
Tips3 cellに表示する情報の削除方法
ここまでで選択肢情報が取得できたので、あとは削除するだけです。
ここで削除するのは
- TableViewのセルそのものの削除
- TableViewのセルに表示する情報の削除
の2つが必要になります。
- TableViewのセルそのものの削除
これは簡単です。
TableViewはdeleteRows
メソッドを持っているので、取得した選択肢情報を引数に渡してあげれば削除できます。
tableView.deleteRows(at: tableView.indexPathsForSelectedRows, with: UITableViewRowAnimation.automatic)
- TableViewのセルに表示する情報の削除
こっちが少し厄介です。
たとえば削除対象の情報を配列で持っているケースの場合、for文を使ってremove
で要素を順番に削除することが考えられますが、
indexPathsForSelectedRows
をそのまま渡して削除しようとすると、チェックした順番でindexPathが格納されているため、removeする度に削除対象の要素番号がズレてしまいます。
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のズレが起こることはなくなります。
let sortedIndexPaths = indexPathsForSelectedRows.sorted { $0.row > $1.row }
for indexPathList in sortedIndexPaths {
deleteArray.remove(at: indexPathList.row)
}
まとめ
以上がTableViewのセルを一気に複数削除する上でのTipsになります。
ソースコードはGithub上にあがっているので参考にしてみて下さい。