【Xcode/Swift】CustomTableViewCellの実装で気をつけること

サンプルプロジェクト & 問題点

カスタムセルを使ってTableViewにUIImageとUILabelを表示させる

ビルドも正常に出来ていて問題がないように見えますが、一点修正すべき部分があります。

それが、

CustomTableViewCell内のUIパーツをViewController.swift内で処理している

という部分です。

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fruitsDataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as? CustomTableViewCell else {
            return UITableViewCell()
        }
        // CustomTableViewCellのUIパーツを外部で処理しまっている -- ここから
        cell.fruitsImageView.image = UIImage(named: fruitsDataArray[indexPath.row].fruitsImageName)
        cell.fruitsNameLabel.text = fruitsDataArray[indexPath.row].fruitsName
        // CustomTableViewCellのUIパーツを外部で処理しまっている -- ここまで
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return self.view.frame.height / 8.5
    }

}

これはViewのライフサイクル上望ましくなく、基本的にはそのUIパーツを紐づけているファイル内で処理をさせるようにしなければなりません。


修正方法

CustomCell内の関数に引数を持たせて、そこにIndexPath.row番目の情報を渡してあげるという形にしてあげましょう

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fruitsDataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as? CustomTableViewCell else {
            return UITableViewCell()
        }
        // CustomCell内の関数に引数を持たせて、そこにIndexPath.row番目の情報を渡す
        cell.configureCustomCell(fruitsImageName: fruitsDataArray[indexPath.row].fruitsImageName, fruitsName: fruitsDataArray[indexPath.row].fruitsName)
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return self.view.frame.height / 8.5
    }
import UIKit

class CustomTableViewCell: UITableViewCell {
    @IBOutlet private weak var fruitsImageView: UIImageView!
    @IBOutlet private weak var fruitsNameLabel: UILabel!

    func configureCustomCell(fruitsImageName: String, fruitsName: String) {
        fruitsImageView.image = UIImage(named: fruitsImageName)
        fruitsNameLabel.text = fruitsImageName
    }

}
import UIKit

class ViewController: UIViewController {

    @IBOutlet private weak var tableView: UITableView!

    private var fruitsDataArray: [(fruitsImageName: String, fruitsName: String)] = [
        ("Banana", "バナナ"),
        ("Apple", "リンゴ"),
        ("Grape", "ブドウ"),
        ("Strawberry", "イチゴ"),
        ("Mango", "マンゴー"),
        ("Orange", "オレンジ"),
        ("Pineapple", "パイナップル")
    ]


    override func viewDidLoad() {
        super.viewDidLoad()
        configureTableView()
    }

    private func configureTableView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
    }

}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fruitsDataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as? CustomTableViewCell else {
            return UITableViewCell()
        }
        // CustomCell内の関数に引数を持たせて、そこにIndexPath.row番目の情報を渡す
        cell.configureCustomCell(fruitsImageName: fruitsDataArray[indexPath.row].fruitsImageName, fruitsName: fruitsDataArray[indexPath.row].fruitsName)
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return self.view.frame.height / 8.5
    }

}