Skip to content

Instantly share code, notes, and snippets.

@siemensikkema
Last active April 21, 2023 17:13
Show Gist options
  • Save siemensikkema/4ef5754b4aceeed623fb4e0dd4056852 to your computer and use it in GitHub Desktop.
Save siemensikkema/4ef5754b4aceeed623fb4e0dd4056852 to your computer and use it in GitHub Desktop.
Multiple Cell Types example based on episode 6 of talk.objc.io in response to question in episode 9
import UIKit
import XCPlayground
struct Episode {
var title: String
}
struct Season {
var number: Int
var title: String
}
protocol Reusable: class {
static var reuseIdentifier: String { get }
}
struct Row {
init<Item, Cell: UITableViewCell where Cell: Reusable>(item: Item, reuseIdentifier: String, configure: (Cell, Item) -> (), didSelect: ((Item) -> ())? = nil) {
self.cell = { (tableView, indexPath) in
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! Cell
configure(cell, item)
return cell
}
self.didSelect = { didSelect?(item) }
}
let cell: (from: UITableView, at: NSIndexPath) -> UITableViewCell
let didSelect: () -> ()
}
final class RowsViewController: UITableViewController {
var rows: [Row] = []
init(rows: [Row], cellTypes: [Reusable.Type]) {
super.init(style: .Plain)
cellTypes.forEach { (reusable) in
tableView.registerClass(reusable, forCellReuseIdentifier: reusable.reuseIdentifier)
}
self.rows = rows
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
rows[indexPath.row].didSelect()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return rows[indexPath.row].cell(from: tableView, at: indexPath)
}
}
let sampleEpisodes = [
Episode(title: "First Episode"),
Episode(title: "Second Episode"),
Episode(title: "Third Episode")
]
let sampleSeasons = [
Season(number: 1, title: "Season One"),
Season(number: 2, title: "Season Two")
]
final class SeasonCell: UITableViewCell, Reusable {
static let reuseIdentifier = "SeasonCell"
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .Value1, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
final class CastCell: UITableViewCell, Reusable {
static let reuseIdentifier = "CastCell"
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .Value1, reuseIdentifier: reuseIdentifier)
backgroundColor = .lightGrayColor()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let episodeRows = sampleEpisodes.map { (episode) in
Row(item: episode, reuseIdentifier: SeasonCell.reuseIdentifier, configure: { (cell: SeasonCell, episode) in
cell.textLabel?.text = episode.title
})
}
let nc = UINavigationController()
let pushEpisodes: (Season) -> () = { (season) in
let episodesVC = RowsViewController(rows: episodeRows, cellTypes: [SeasonCell.self])
episodesVC.title = season.title
nc.pushViewController(episodesVC, animated: true)
}
let seasonRows = sampleSeasons.map {
Row(item: $0,
reuseIdentifier: SeasonCell.reuseIdentifier,
configure: { (cell: SeasonCell, season) in
cell.textLabel?.text = season.title
cell.detailTextLabel?.text = "\(season.number)"
},
didSelect: pushEpisodes)
}
let otherRow = Row(item: "Cast", reuseIdentifier: CastCell.reuseIdentifier, configure: { (cell: CastCell, text) in
cell.textLabel?.text = text
})
let seasonsVC = RowsViewController(rows: seasonRows + [otherRow], cellTypes: [SeasonCell.self, CastCell.self])
seasonsVC.tableView.reloadData()
seasonsVC.title = "Seasons"
nc.viewControllers = [seasonsVC]
nc.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
XCPlaygroundPage.currentPage.liveView = nc.view
@siemensikkema
Copy link
Author

I have adapted my solution of supporting multiple cell types in one table view controller to the code from: https://github.com/objcio/S01E06-generic-table-view-controllers in response to the question posed in https://talk.objc.io/episodes/S01E09-q-a. My solution involves extracting the cell configuring logic out of the table view controller by introducing a Row type. This allows the table view controller to be non-generic and therefore not restricted to one cell type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment