はじめに
UITableViewCellのパターンが増えると、カオスになりがちな処理を
enumを利用して実装してみます。
※よくあるパターンなので、自分の中で一度整理してみます。
enumを利用する理由は、下記の2点です。
- セルタイプのパターンを網羅的に管理するため
- セルタイプのパターン毎に処理を纏めて管理するため
今回ご説明する例
下記の3パターンのセルを出し分ける例でご説明します。
- データが0件の場合は、「該当するデータはありません」と表示する
- 一覧にタイトルを左寄せで表示する
- 一覧にタイトルを中央寄せで表示する
※本当は、全く違うタイプのセルでご説明したほうが良いと思いますが、
お話のメインがUITableViewCellをEnumで管理することなので、
シンプルなセルのタイプでご説明します。
今回ご説明しないこと
Storyboardを利用したTableViewやTableViewCellを生成する部分は、
割愛させていただきます。
実装手順
下記のとおりです。
- セルタイプのパターンをenumで定義する
- 一覧表示管理用の構造体を定義する
- パターン毎にTableViewCellを定義する
- enumにパターン毎の各種処理を定義する
- セルタイプ取得用のクラスを定義する
- UITableView生成用のクラスを定義する
- 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が肥大化していく問題は別途考えたいと思います。