6
4

More than 3 years have passed since last update.

【Swift5】UITableViewCellで画像や色を変えたのにも関わらず同じセルが表示されるバグの対処法

Posted at

はじめに

UItableViewCellのセルの再利用の仕組みによって、指定したcellの見た目を変える処理をした際に、過去のセルの変更が残ってしまうというちょっとしたバグが発生したので今回はUItableviewのセルの仕組みをみていきます

セルの再利用

TableViewは、大量にある同じ種類の情報をリスト表示する際に利用されます。そのため実装方法によってはセルの描画数が非常に多くなり、特にスクロール時には、パフォーマンスの低下が考えられます。これを解決するのがcellの再利用です。毎回新しくviewを作るのではなく以前に生成したcellを利用することで、メモリ割り当てを最小限にできます。
まずセルの再利用の際にはUItableviewにデフォルトで設定されているこのメソッドを使います

公式ドキュメント参照
https://developer.apple.com/documentation/uikit/uitableview/1614891-dequeuereusablecell

func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell

そしてこのメソッドを、cellの生成されるタイミングで上のメソッドを発火させます

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//処理
}

このメソッドは、再利用可能なcellがあればそれを、なければ新しく作成したcellを返します。再利用可能なcellは、reuseIdentifierに紐づけられたreuse queueという場所に格納されています。

Reuse Queue

tableviewは裏側でreuse queueという、データの箱のようなものを持っており、reuseIdentifierごとにreuse queueが存在します。画面外に出たcellは、自身のidentifierに紐づいたreuse queueに追加されます。そして、同じidentifierのcellが表示されようとする時、queueから取り出されます。
下の図のようにスクロールしたら次のcellがqueueから取り出されるイメージ
dequeue.png

セルの再利用の落とし穴

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得(セルの再利用メソッド発火)
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath)
        print(indexPath.row)
        //セルのviewを変更する処理
        cell.textLabel!.text = "\(self.data[indexPath.row])"
        if indexPath.row == 3{
            cell.backgroundColor = UIColor.red
        }

        return cell
    }

例えばこんな感じで指定したセルの色を変えたい時にこのような処理を書くと、背景色を赤に変えたセルは、他のセルにも再利用されてしまいます。
指定した以外の色は元に戻す必要があります

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得(セルの再利用メソッド発火)
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath)
        print(indexPath.row)
        //セルのviewを変更する処理
        cell.textLabel!.text = "\(self.data[indexPath.row])"
        if indexPath.row == 3{
            cell.backgroundColor = UIColor.red
        } else {
            cell.backgroundColor = 元の色               
        }

        return cell
    }

カスタムセルの中身の画像を動的に変更したい場合はこのようにカスタムセルの画像を初期化する必要があります

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得(セルの再利用メソッド発火)
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath)
        //画像を初期化
        cell.sampleimage.image = nil
        return cell
    }

最後に

セルを再利用してることで前の状態が残るので、想定とは異なるview状態になります
今後似たような症状が起きた場合は、再利用によるセルリセットし忘れを疑いましょう

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4