LoginSignup
1
0

More than 5 years have passed since last update.

多様なセルごちゃ混ぜ UITableView

Last updated at Posted at 2018-06-29

型を値として扱ってると, キャストしづらい...

/// 素直に `protocol 名前: class where Self: 継承元` とやると,
/// 「Redundant constraint 'Self' : 'AnyObject'」
/// とか言われるので, とりあえず回避してみた.
protocol AnyObjectProtocol: class {}

/// とあるビューコントローラに表示するセルは, このプロトコルに準拠する.
protocol MyTableViewCellProtocol: AnyObjectProtocol where Self: UITableViewCell {
    /// .xib ファイルの名前.
    static var XIB_NAME: String { get }

    /// Re-use Identifier.
    static var IDENTIFIER: String { get }

    /// 各種類別の, セルの高さ.
    static var HEIGHT: CGFloat { get }

    /// セルにデータをセットする, 共通のメソッド.
    func setMyTableViewData(_ data: String)
}
extension MyTableViewCellProtocol {
    ///
    /// 型を値として扱っているけれど, キャストしたい.
    ///
    /// こんな方法しか思い付かなかったが,
    /// もう少しスマートな方法があるのだろうか ?
    ///
    static func cast(from: UITableViewCell) -> Self? {
        return from as? Self
    }
}

/// Foo タイプのセル.
class MyTableViewFooCell: UITableViewCell, MyTableViewCellProtocol {
    static var XIB_NAME: String = "MyTableViewFooCell"
    static var IDENTIFIER: String = "MyTableViewFooCell"
    static var HEIGHT: CGFloat = 30
    func setMyTableViewData(_ data: String) {}
}

/// Bar タイプのセル.
class MyTableViewBarCell: UITableViewCell, MyTableViewCellProtocol {
    static var XIB_NAME: String = "MyTableViewBarCell"
    static var IDENTIFIER: String = "MyTableViewBarCell"
    static var HEIGHT: CGFloat = 80
    func setMyTableViewData(_ data: String) {}
}

/// Baz タイプのセル.
class MyTableViewBazCell: UITableViewCell, MyTableViewCellProtocol {
    static var XIB_NAME: String = "MyTableViewBazCell"
    static var IDENTIFIER: String = "MyTableViewBazCell"
    static var HEIGHT: CGFloat = 50
    func setMyTableViewData(_ data: String) {}
}

/// 多種のセルをごちゃ混ぜに持つテーブルビューを持つビューコントローラ.
class TestViewController: UIViewController
    , UITableViewDelegate
    , UITableViewDataSource
{
    /// セルのデータ.
    private struct CellData {
        /// セルのクラス.
        var cellClass: MyTableViewCellProtocol.Type

        /// セルのデータ.
        var data: String
    }

    /// 便宜上ここにベタ書きするが, 本来は外部データを元に生成.
    private let cellData: [CellData] = [
        CellData(cellClass: MyTableViewFooCell.self,
                 data: "Foo セルのデータ1."),
        CellData(cellClass: MyTableViewBarCell.self,
                 data: "Bar セルのデータ1."),
        CellData(cellClass: MyTableViewBarCell.self,
                 data: "Bar セルのデータ2."),
        CellData(cellClass: MyTableViewFooCell.self,
                 data: "Foo セルのデータ2."),
        CellData(cellClass: MyTableViewBazCell.self,
                 data: "Baz セルのデータ1."),
        CellData(cellClass: MyTableViewBarCell.self,
                 data: "Bar セルのデータ3."),
        CellData(cellClass: MyTableViewBarCell.self,
                 data: "Bar セルのデータ4."),
    ]

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let cell_classes: [MyTableViewCellProtocol.Type] = [
            MyTableViewFooCell.self,
            MyTableViewBarCell.self,
            MyTableViewBazCell.self ]
        for cell_class in cell_classes {
            tableView.register(
                UINib(nibName: cell_class.XIB_NAME,
                      bundle: nil),
                forCellReuseIdentifier: cell_class.IDENTIFIER)
        }
    }

    func tableView(_: UITableView, numberOfRowsInSection: Int) -> Int {
        return cellData.count
    }

    func tableView(_: UITableView,
                   heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return cellData[indexPath.row].cellClass.HEIGHT
    }

    func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell_data = cellData[indexPath.row]
        let cell = tableView.dequeueReusableCell(
            withIdentifier: cell_data.cellClass.IDENTIFIER,
            for: indexPath)
        if let my_cell = cell_data.cellClass.cast(from: cell) {
            my_cell.setMyTableViewData(cell_data.data)
        }
        return cell
    }
}
1
0
1

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
1
0