初めに
この記事では、UICollectionView
にて複数アイテムを選択する方法を紹介します!
全体のソースコードはこちらです🔨
https://github.com/h-taro/CollectionViewSample
環境
Xcode 12.5.1
Swift 5.4.2
macOS Big Sur 11.5.2
実装方法
大まかな実装方法は以下です!
-
UICollectionView
で複数選択できるように設定する - 選択されたアイテムを保存する配列を準備する。(今回の実装の場合
selectedNumbers
) - アイテムがタップされた時に
selectedNumbers
へ追加する - 選択されたアイテムがもう一度タップされた場合、
selectedNumbers
から削除する
該当箇所のコードはこちらです!🚀
ViewController.swift
import UIKit
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
...
// CollectionViewで複数選択できるように設定する
collectionView.allowsMultipleSelection = true
}
}
// MARK:- UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numbers.count
}
// cell初期化時に選択されているか評価する。
// 選択されている場合は、ハイライトする。そうでない場合は、デフォルトのスタイルを当てる。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
let number = numbers[indexPath.row]
cell.setupLabel(text: String(number))
if isSelected(number: number) {
cell.changeBorder()
} else {
cell.initBorder()
}
return cell
}
}
// MARK:- UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
// 選択された時にselectedNumbersにアイテムを追加する
// 選択された時のスタイルを当てる
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard
let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell
else { return }
addSelectedNumber(number: numbers[indexPath.row])
DispatchQueue.main.async {
cell.changeBorder()
}
}
// 選択解除時された時にselectedNumbersからアイテムを削除する
// スタイルを元に戻す
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
guard
let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell
else { return }
removeSelectedNumber(number: numbers[indexPath.row])
DispatchQueue.main.async {
cell.initBorder()
}
}
}
extension ViewController {
...
private func addSelectedNumber(number: Int) {
selectedNumbers.append(number)
}
private func removeSelectedNumber(number: Int) {
selectedNumbers = selectedNumbers.filter({ selectedNumber in
return selectedNumber != number
})
}
private func isSelected(number: Int) -> Bool {
return selectedNumbers.contains(number)
}
}
注意点
選択状況の制御について注意点があります。
CollectionView
ではセルが再利用されます。
そのため、スクロールした時に選択状況まで再利用され、
よく分からない状態になってしまいます。
なので、今回の実装ではセルが初期化されるたびに選択状況を評価し、
結果に応じてスタイルを変更するようにしました!
ViewController.swift
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
let number = numbers[indexPath.row]
// 選択されているなら、ハイライト。そうでないなら、デフォルトのスタイルを当てる。
cell.setupLabel(text: String(number))
if isSelected(number: number) {
cell.changeBorder()
} else {
cell.initBorder()
}
return cell
}
最後に
今回のようなCollectionView
にて複数アイテムを選択したい時は結構あると思います。
しかし、実際に実装してみると思い通りにならない箇所が多々ありました。
特に、セルが再利用されるときに選択状況まで再利用される箇所ははまりどころでした。
参考にしていただけると嬉しいです!🙏