LoginSignup
16
18

More than 5 years have passed since last update.

[Swift]UITableViewCellをEnumで管理する

Last updated at Posted at 2016-11-23

はじめに

UITableViewCellのパターンが増えると、カオスになりがちな処理を
enumを利用して実装してみます。

※よくあるパターンなので、自分の中で一度整理してみます。

enumを利用する理由は、下記の2点です。

  1. セルタイプのパターンを網羅的に管理するため
  2. セルタイプのパターン毎に処理を纏めて管理するため

今回ご説明する例

下記の3パターンのセルを出し分ける例でご説明します。

  1. データが0件の場合は、「該当するデータはありません」と表示する
  2. 一覧にタイトルを左寄せで表示する
  3. 一覧にタイトルを中央寄せで表示する

※本当は、全く違うタイプのセルでご説明したほうが良いと思いますが、
 お話のメインがUITableViewCellをEnumで管理することなので、
 シンプルなセルのタイプでご説明します。

今回ご説明しないこと

Storyboardを利用したTableViewやTableViewCellを生成する部分は、
割愛させていただきます。

実装手順

下記のとおりです。

  1. セルタイプのパターンをenumで定義する
  2. 一覧表示管理用の構造体を定義する
  3. パターン毎にTableViewCellを定義する
  4. enumにパターン毎の各種処理を定義する
  5. セルタイプ取得用のクラスを定義する
  6. UITableView生成用のクラスを定義する
  7. UIViewControllerから利用してみる

Let'S Try

1. セルタイプのパターンをenumで定義する

3パターンのセルを定義します。

CellType.swift
import UIKit

enum CellType {
    case textAliginLeft
    case textAliginCenter
    case noData
}

2. 一覧表示管理用の構造体を定義する

Item.swift
struct Item {
    var title = ""
    var type = CellType.noData
}

3. パターン毎にTableViewCellを定義する

3.1. TableViewCellのリユースIDを定義する

今回は、クラス名とリユースIDを同名とするため、下記のように定義します。

UITableViewCell+Helper.swift
import UIKit

protocol UITableViewCellType {
    static var reuseIdentifier: String { get }
}

extension UITableViewCell: UITableViewCellType {
    internal static var reuseIdentifier: String {
        return String(describing: self)
    }
}

3.2. 一覧用のTableViewCellを作る

タイトル表示用のラベルがあるだけのセルです。

TextAlignLeftTableViewCell.swift
import UIKit

final class TextAlignLeftTableViewCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!

    func configForCell(item: Item) {
        titleLabel.text = item.title
    }
}
TextAlignCenterTableViewCell.swift
import UIKit

final class TextAlignCenterTableViewCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!

    func configForCell(item: Item) {
        titleLabel.text = item.title
    }
}

3.3. データが0件の場合のTableViewCellを作る

タイトル表示用のラベルがあるだけのセルです。

ListTableViewCell.swift
import UIKit

final class NoDataTableViewCell: UITableViewCell {    
    @IBOutlet weak var titleLabel: UILabel!

    func configForCell(title: String) {
        titleLabel.text = title
    }
}

4. enumにパターン毎の各種処理を定義する

CellType.swift
import UIKit

enum CellType {
    case textAliginLeft
    case textAliginCenter
    case noData

    // セルタイプ毎のTableViewCellのインスタンスを取得する
    func cell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {

        switch self {
        case .textAliginLeft:
            return tableView.dequeueReusableCell(withIdentifier: TextAlignLeftTableViewCell.reuseIdentifier,
                                                 for: indexPath as IndexPath) as! TextAlignLeftTableViewCell

        case .textAliginCenter:
            return tableView.dequeueReusableCell(withIdentifier: TextAlignCenterTableViewCell.reuseIdentifier,
                                                 for: indexPath as IndexPath) as! TextAlignCenterTableViewCell

        case .noData:
            return tableView.dequeueReusableCell(withIdentifier: NoDataTableViewCell.reuseIdentifier,
                                                 for: indexPath as IndexPath) as! NoDataTableViewCell
        }
    }

    // セルタイプ毎に値を設定する
    func configCellForList(cell: UITableViewCell,
                               indexPath: IndexPath,
                               items: [Item]) {

        switch self {
        case .textAliginLeft:
            let cell = cell as! TextAlignLeftTableViewCell
            cell.configForCell(item: items[indexPath.row])

        case .textAliginCenter:
            let cell = cell as! TextAlignCenterTableViewCell
            cell.configForCell(item: items[indexPath.row])

        case .noData:
            let cell = cell as! NoDataTableViewCell
            cell.configForCell(title: "該当するデータはありません")
        }
    }

    // セルタイプ毎にセルの高さを取得する
    func heightForRow() -> CGFloat {

        switch self {
        case .textAliginLeft, .textAliginCenter:
            return 44.0
        default:
            return UIScreen.main.bounds.height
        }
    }
}

5. セルタイプ取得用のクラスを定義する

データ件数が0件の場合は、noDataセルを利用します。
それ以外は、指定したセルを利用します。

UITableView+CellType.swift
import UIKit

extension UITableView {

    func cellType(indexPath: IndexPath, items: [Item]) -> CellType {
        if items.count == 0 { return .noData }
        return items[indexPath.row].type
    }
}

6. UITableView生成用のクラスを定義する

ListDataSource.swift
import UIKit

final class ListDataSource:NSObject, UITableViewDataSource {

    fileprivate var items: [Item] = []

    func setItems(items: [Item]) {
        self.items = items
    }

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int {        
        return items.count == 0 ? 1 : items.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cellType = tableView.cellType(indexPath: indexPath,
                                              items: items)
        let cell = cellType.cell(tableView: tableView,
                                 indexPath: indexPath)
        cellType.configCellForList(cell: cell,
                              indexPath: indexPath,
                                  items: items)
        return cell
    }
}

7. UIViewControllerから利用してみる

UIViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    let dataSource = ListDataSource()
    var items: [Item] = [
        Item(title: "title1", type: CellType.textAliginLeft),
        Item(title: "title2", type: CellType.textAliginCenter)
    ]
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        loadList()
    }

    fileprivate func setup() {
        tableView.dataSource = dataSource
        tableView.delegate = self
    }

    fileprivate func loadList() {
        dataSource.setItems(items: items)
    }
}

//MARK: - UITableViewDelegate

extension ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView,
                   heightForRowAt indexPath: IndexPath) -> CGFloat {

        let cellType = tableView.cellType(indexPath: indexPath,
                                              items: items)
        return cellType.heightForRow()
    }
}

まとめ

新しいUITableViewCellのパターンが増えると、
該当のTableViewCellとenumを拡張していくイメージです。

拡張していってもUIViewControllerは肥大化しないし、
enumで定義しているので追加漏れもありません。

enumが肥大化していく問題は別途考えたいと思います。

16
18
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
16
18