😺

UICollectionView(UITableView)のセルの選択状態を制御しよう

2022/10/13に公開

UICollectionView のセルの選択状態の制御に関して、この一年ぐらい何回かハマって、毎回同じ解決法を見つけてることに気づいたので、さすがに書きます。

選択状態とは

UICollectionView のセルをタップしたとき、そのセルは選択状態になります。
基本的にはUICollectionView がいい感じにハンドリングしてくれます。
開発者がそのステータスを見たい場合、UICollectionViewCell に、isSelected というプロパティを見ます。

セルをタップしたときに背景色を変えるやつ

よくあるisSelected のユースケースとしては、ユーザーがセルをタップしたときに、その背景色を変えてあげる、というのがあると思います。
カスタムセルをつくっている場合、こんな感じで指定します。

final class CustomCell: UICollectionViewCell {
    override var isSelected: Bool {
        didSet {
            contentView.backgroundColor = isSelected
                ? .lightGray
                : .clear
        }
    }
}

画面遷移のときの isSelected

基本的にはUICollectionView がいい感じにステータス管理してくれるんですが、画面遷移のときに想定と違う挙動になりました。
セルをタップして、詳細画面に遷移する、というのはよくある動きだと思うんですが、
そのときに元の画面に戻ると、選択されている状態で戻ることになります。

これに気づいたとき、当初はバグかと思ったんですが、よくよく考えてみると、たぶんUIKitの想定している挙動だと思われます。
別画面から戻ったときに、前に選択したセルを残しておく、というのは、Webサイトでもたまに見ます。
おそらくはUIKitもそういう思想なんだと思います。

でも残したくないよね?

前の選択状態が残ってた方が親切なときもあるかとは思うんですが、多くの場合、UI/UX的にちょっと不自然かなと思います。
なので、画面遷移前の選択状態を消す方法を模索します。

UICollectionViewController を使っている場合

Appleも前回表示の選択状態を消したいときがあるというケースがあることは把握していて、その選択肢も用意されています。
もしUICollectionViewController を使っていれば、clearsSelectionOnViewWillAppear という長い名前のフラグをオフるだけですみます。

https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623977-clearsselectiononviewwillappear

Viewしか使っていない場合

UICollectionViewController は使ってないけど、UICollectionView を使っているよ、という状況もあると思います。
(僕はそうでした)

最初勘違いして、clearsContextBeforeDrawing というプロパティを操作したんですが、これは全然別のプロパティでした。
UIView が持っているプロパティで、もうちょっと低レイヤーの話っぽい)

一番手っ取り早いのが、deselectItem(at:animated:)を使う方法だと思います。
実装例としては、こんな感じ。

func collectionView(_: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    defer {
        collectionView.deselectItem(at: indexPath, animated: true)
    }
    
    // …
}

viewWillAppear(animated:)  の中で、全セルにこのメソッド使うでも同じことができますので、どちらがいいかはお好みで。

UITableView でもできる

詳細は下記の記事を見て欲しいですが、UITableView でも同じことができます。

https://egg-is-world.com/2020/07/11/tableview-deselect/

(了)

Discussion