├── TableViewsWithMultipleCells.playground ├── Pages │ └── Initial.xcplaygroundpage │ │ ├── timeline.xctimeline │ │ └── Contents.swift ├── playground.xcworkspace │ └── contents.xcworkspacedata └── contents.xcplayground └── README.md /TableViewsWithMultipleCells.playground/Pages/Initial.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TableViewsWithMultipleCells.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Talk 2 | ## Generic Table View Controllers (Part 2) 3 | 4 | This is the code that accompanies Swift Talk Episode 26: [Generic Table View Controllers (Part 2)](https://talk.objc.io/episodes/S01E26-generic-table-view-controllers-part-2) 5 | -------------------------------------------------------------------------------- /TableViewsWithMultipleCells.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TableViewsWithMultipleCells.playground/Pages/Initial.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import PlaygroundSupport 3 | 4 | 5 | struct Album { 6 | var title: String 7 | } 8 | 9 | struct Artist { 10 | var name: String 11 | } 12 | 13 | 14 | struct CellDescriptor { 15 | let cellClass: UITableViewCell.Type 16 | let reuseIdentifier: String 17 | let configure: (UITableViewCell) -> () 18 | 19 | init(reuseIdentifier: String, configure: @escaping (Cell) -> ()) { 20 | self.cellClass = Cell.self 21 | self.reuseIdentifier = reuseIdentifier 22 | self.configure = { cell in 23 | configure(cell as! Cell) 24 | } 25 | } 26 | } 27 | 28 | final class ItemsViewController: UITableViewController { 29 | var items: [Item] = [] 30 | let cellDescriptor: (Item) -> CellDescriptor 31 | var didSelect: (Item) -> () = { _ in } 32 | var reuseIdentifiers: Set = [] 33 | 34 | init(items: [Item], cellDescriptor: @escaping (Item) -> CellDescriptor) { 35 | self.cellDescriptor = cellDescriptor 36 | super.init(style: .plain) 37 | self.items = items 38 | } 39 | 40 | required init?(coder aDecoder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 45 | let item = items[indexPath.row] 46 | didSelect(item) 47 | } 48 | 49 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 50 | return items.count 51 | } 52 | 53 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 54 | let item = items[indexPath.row] 55 | let descriptor = cellDescriptor(item) 56 | 57 | if !reuseIdentifiers.contains(descriptor.reuseIdentifier) { 58 | tableView.register(descriptor.cellClass, forCellReuseIdentifier: descriptor.reuseIdentifier) 59 | reuseIdentifiers.insert(descriptor.reuseIdentifier) 60 | } 61 | 62 | let cell = tableView.dequeueReusableCell(withIdentifier: descriptor.reuseIdentifier, for: indexPath) 63 | descriptor.configure(cell) 64 | return cell 65 | } 66 | } 67 | 68 | 69 | final class ArtistCell: UITableViewCell { 70 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 71 | super.init(style: .value1, reuseIdentifier: reuseIdentifier) 72 | } 73 | 74 | required init?(coder aDecoder: NSCoder) { 75 | fatalError("init(coder:) has not been implemented") 76 | } 77 | } 78 | 79 | final class AlbumCell: UITableViewCell { 80 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 81 | super.init(style: .value2, reuseIdentifier: reuseIdentifier) 82 | } 83 | 84 | required init?(coder aDecoder: NSCoder) { 85 | fatalError("init(coder:) has not been implemented") 86 | } 87 | } 88 | 89 | 90 | let artists: [Artist] = [ 91 | Artist(name: "Prince"), 92 | Artist(name: "Glen Hansard"), 93 | Artist(name: "I Am Oak") 94 | ] 95 | 96 | let albums: [Album] = [ 97 | Album(title: "Blue Lines"), 98 | Album(title: "Oasem"), 99 | Album(title: "Bon Iver") 100 | ] 101 | 102 | enum RecentItem { 103 | case artist(Artist) 104 | case album(Album) 105 | } 106 | 107 | let recentItems: [RecentItem] = [ 108 | .artist(artists[0]), 109 | .artist(artists[1]), 110 | .album(albums[1]) 111 | ] 112 | 113 | extension Artist { 114 | func configureCell(_ cell: ArtistCell) { 115 | cell.textLabel?.text = name 116 | } 117 | } 118 | 119 | extension Album { 120 | func configureCell(_ cell: AlbumCell) { 121 | cell.textLabel?.text = title 122 | } 123 | } 124 | 125 | extension RecentItem { 126 | var cellDescriptor: CellDescriptor { 127 | switch self { 128 | case .artist(let artist): 129 | return CellDescriptor(reuseIdentifier: "artist", configure: artist.configureCell) 130 | case .album(let album): 131 | return CellDescriptor(reuseIdentifier: "album", configure: album.configureCell) 132 | } 133 | } 134 | } 135 | 136 | let recentItemsVC = ItemsViewController(items: recentItems, cellDescriptor: { $0.cellDescriptor }) 137 | 138 | let nc = UINavigationController(rootViewController: recentItemsVC) 139 | 140 | nc.view.frame = CGRect(x: 0, y: 0, width: 200, height: 300) 141 | PlaygroundPage.current.liveView = nc.view 142 | 143 | 144 | --------------------------------------------------------------------------------