Created
November 22, 2019 13:39
-
-
Save descorp/8017d3285241de672de492d1b37d2ee9 to your computer and use it in GitHub Desktop.
Swift Universal Table handlers: allows to load and manage table collections for single type and multiple types of cells
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
class BaseTableHandler<T> : NSObject, UITableViewDelegate, UITableViewDataSource { | |
typealias TableSection = (name: String?, items: [T]) | |
typealias Action = (T)->Void | |
var collection: [TableSection] = [] | |
weak var table: UITableView? | |
private let selectAction: Action? | |
private let deleteAction: Action? | |
init(collection: [T], delete: Action? = nil, select: Action? = nil) { | |
self.collection = [(name: nil, items: collection)] | |
self.selectAction = select | |
self.deleteAction = delete | |
super.init() | |
} | |
init(collection: [TableSection], delete: Action? = nil, select: Action? = nil) { | |
self.collection = collection | |
self.selectAction = select | |
self.deleteAction = delete | |
super.init() | |
} | |
public func register(table: UITableView) { | |
self.table = table | |
table.dataSource = self | |
table.delegate = self | |
fillEmptySpaceWithFooter = true | |
} | |
var fillEmptySpaceWithFooter: Bool { | |
get { | |
return table?.tableFooterView != nil | |
} | |
set { | |
if newValue { | |
table?.tableFooterView = UIView() | |
} else { | |
table?.tableFooterView = nil | |
} | |
} | |
} | |
func numberOfSections(in tableView: UITableView) -> Int { | |
return collection.count | |
} | |
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return collection[section].items.count | |
} | |
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
preconditionFailure("This is abstract method") | |
} | |
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | |
return collection[section].name | |
} | |
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | |
tableView.deselectRow(at: indexPath, animated: true) | |
self.selectAction?(self.collection[indexPath.section].items[indexPath.item]) | |
} | |
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { | |
return deleteAction == nil ? .none : .delete | |
} | |
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { | |
return deleteAction != nil | |
} | |
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { | |
if (editingStyle == .delete) { | |
deleteAction?(self.collection[indexPath.section].items[indexPath.item]) | |
} | |
} | |
} | |
extension BaseTableHandler where T: Equatable { | |
func remove(item: T) { | |
guard | |
let theSection = self.collection.firstIndex(where: { $0.items.contains(item) }), | |
let index = self.collection[theSection].items.firstIndex(of: item) | |
else { return } | |
self.collection[theSection].items.remove(at: index) | |
table?.beginUpdates() | |
table?.reloadSections([theSection], with: .fade) | |
table?.endUpdates() | |
} | |
func updateCollectionWith(item: T) { | |
guard | |
let theSection = self.collection.firstIndex(where: { $0.items.contains(item) }), | |
let index = self.collection[theSection].items.firstIndex(of: item) | |
else { return } | |
self.collection[theSection].items[index] = item | |
table?.beginUpdates() | |
table?.reloadRows(at: [IndexPath(item: index, section: theSection)], with: .fade) | |
table?.endUpdates() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
final class MulticellTableHandler: BaseTableHandler<TableCellGenerator> { | |
public override func register(table: UITableView) { | |
super.register(table: table) | |
let cellTypes = self.collection.flatMap { $0.items.map { $0.identifier } }.map(TypeHolder.init) | |
Set(cellTypes).forEach { table.register($0.type, forCellReuseIdentifier: $0.name) } | |
} | |
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let cellBuilder = self.collection[indexPath.section].items[indexPath.item] | |
return cellBuilder.generate(tableView: tableView, for: indexPath) | |
} | |
private struct TypeHolder: Hashable { | |
let type: AnyClass | |
var name: String { | |
return String(describing: type) | |
} | |
func hash(into hasher: inout Hasher) { | |
hasher.combine(name) | |
} | |
static func == (lhs: TypeHolder, rhs: TypeHolder) -> Bool { | |
return lhs.name == rhs.name | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
protocol TableCellGenerator: class { | |
var identifier: UITableViewCell.Type { get } | |
func generate(tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell | |
func registerCell(in tableView: UITableView) | |
} | |
public protocol CellBuilder { | |
associatedtype CellType: UITableViewCell | |
func build(_ cell: CellType) | |
} | |
extension TableCellGenerator { | |
var reusableIdentifier: String { | |
return String(describing: identifier) | |
} | |
func registerCell(in tableView: UITableView) { | |
tableView.register(identifier, forCellReuseIdentifier: reusableIdentifier) | |
} | |
} | |
extension TableCellGenerator where Self: CellBuilder { | |
func generate(tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell { | |
guard let cell = tableView.dequeueReusableCell(withIdentifier: reusableIdentifier, for: indexPath) as? Self.CellType else { | |
return UITableViewCell() | |
} | |
self.build(cell) | |
return cell | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
protocol TableCell: UITableViewCell { | |
associatedtype ViewModel | |
func update(with viewModel: ViewModel) | |
static var cellIdentifier: String { get } | |
static func registerCell(in tableview: UITableView) | |
} | |
extension TableCell { | |
static var cellIdentifier: String { | |
return String(describing: self) | |
} | |
static func registerCell(in table: UITableView) { | |
table.register(self, forCellReuseIdentifier: cellIdentifier) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
final class UniversalTableHandler<T, Cell: TableCell> : BaseTableHandler<T> where Cell.ViewModel == T { | |
public override func register(table: UITableView) { | |
super.register(table: table) | |
Cell.registerCell(in: table) | |
} | |
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
guard let cell = tableView.dequeueReusableCell(withIdentifier: Cell.cellIdentifier) as? Cell else { | |
return UITableViewCell(style: .default, reuseIdentifier: Cell.cellIdentifier) | |
} | |
let item = self.collection[indexPath.section].items[indexPath.item] | |
cell.update(with: item) | |
return cell | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment