Skip to content

Instantly share code, notes, and snippets.

@pejalo
Last active April 18, 2018 17:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pejalo/678e32054b889f70e5fd968955a3b52c to your computer and use it in GitHub Desktop.
Save pejalo/678e32054b889f70e5fd968955a3b52c to your computer and use it in GitHub Desktop.
Attempt to define UITableViews more declaratively
//
// TableViewDataSource.swift
//
import UIKit
//////////////////////////////
// Project code
//////////////////////////////
// MARK: -
/**
This is how I want to define UITableViews. Each section only needs one type of reusable cell.
This table view has two sections: One for string-based views, and one for color-based views.
*/
class MyStringAndColorTableView: UITableView {
let stringValues = ["First", "Second"]
let colorValues: [UIColor] = [.blue, .brown]
init() {
super.init(frame: .zero, style: .plain)
let stringSection = TableViewSection(values: stringValues, reuseIdentifier: UILabel.reuseIdentifier) { (string, view) -> UIView in
let label = view as? UILabel ?? UILabel()
label.text = string
return label
}
let colorSection = TableViewSection(values: colorValues, reuseIdentifier: UIView.reuseIdentifier) { (color, view) -> UIView in
let view = view ?? UIView()
view.backgroundColor = color
return view
}
// Current error in Xcode: Heterogeneous collection literal could only be interred to '[Any]'; add explicit type annotation if this is intentional
let sections = [stringSection, colorSection]
// Current error in Xcode: Cannot convert value of type '[Any]' to expected argument type '[TableViewSection<Any>]'
let dataSource = TableViewDataSource(sections: sections)
self.dataSource = dataSource
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//////////////////////////////
// Library code
//////////////////////////////
// MARK: -
struct TableViewSection<GenericValue> {
let values: [GenericValue]
let reuseIdentifier: String
let viewAt: (_ value: GenericValue, _ view: UIView?) -> UIView
}
// MARK: -
class TableViewDataSource: NSObject, UITableViewDataSource {
// The crux of the problem: How to define an array whose content's types contain multiple different generics?
var sections = [TableViewSection<Any>]()
init(sections: [TableViewSection<Any>]) {
self.sections = sections
}
// UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let tableViewSection = sections[section]
return tableViewSection.values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tableViewSection = sections[indexPath.section]
let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: tableViewSection.reuseIdentifier)
let dequeuedView = (dequeuedCell as? TableViewCell)?.view ?? dequeuedCell
let value = tableViewSection.values[indexPath.row]
let view = tableViewSection.viewAt(value, dequeuedView)
return view as? UITableViewCell ?? TableViewCell(view: view)
}
}
// MARK: -
/**
This convenience UITableViewCell subclass allows you to create cells from simple UIViews.
*/
class TableViewCell: UITableViewCell {
var view: UIView
init(view: UIView) {
self.view = view
super.init(style: .default, reuseIdentifier: type(of: view).reuseIdentifier)
contentView.addSubview(view)
// Pin edges of view to contentView via autolayout...
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: -
extension UIView {
static var reuseIdentifier: String { return String(describing: self) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment