はじめに
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から取り出されるイメージ
セルの再利用の落とし穴
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状態になります
今後似たような症状が起きた場合は、再利用によるセルリセットし忘れを疑いましょう