はじめに
こんにちは。
今回は、私がTableViewを使うプロジェクトでよく使っている、UIView
とUIViewCell
の共通化について書きます。
UIView
とUIViewCell
で同じようなデザインを使うことが多くて、この方法を見つけたときは感動しました。
TableView
があるプロジェクトでは、ほぼ必ず使ってます。
ViewとCellの共通化が有効な場面
- UITableViewのヘッダーにテーブルの項目タイトルをつけたい時
- 通常はViewとして使う部品をTableViewのCellとしても利用したい
実装
仕組み作り
こんな感じで、プロトコルを1つ作ります。
CellとViewで共通利用したいカスタムビューにこのプロトコルを継承させればOKです。
extension
に定義したidentifier
は、Cellをインスタンス化するときに使えるので、実装しておきます。
public protocol CellViewBinder {
associatedtype ContentView: UIView
static func contentView() -> ContentView
static var identifier: String { get }
}
extension CellViewBinder {
static var identifier: String {
return String(describing: self)
}
}
こっちがUIViewを継承したカスタムビューをUICellとして利用するために作るクラスです。
class GenericTableViewCell<View>: UITableViewCell where View: CellViewBinder, View.ContentView == View {
let customView: View = .init(frame: .zero)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
contentView.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
customView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
}
customView
に自分の使いたいカスタムビューが入る構造です。
CellViewBinder
プロトコルを継承しているカスタムビューだけが、GenericTableViewCell
にビューを突っ込めるという仕様ですね。やはりプロトコル優秀すぎる。
実際に使う時
// Viewで使う時(例えばTableのヘッダーなどで)
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return CustomView()
}
// Cellで使う時
guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomView.identifier) as? GenericTableViewCell<CustomView> else {
return UITableViewCell()
}
// 実際のViewにアクセスするのは、customView経由
let customView = cell.customView