iOS工程师都知道tableview 在开发中是用得最多的,大部分页面几乎都可以用列表来做。但有没发现大量工作其实是重复的比如cell的注册,delegate数据源的匹配,cell的类型转换。接下来,我们需要将这些代理方法为tableview提供数据封装成数据模型。
数据源:dataSource[section[ rows],section[ rows]]

封装DataSource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Section<Item> {
var items: [Item]
}

struct DataSource<Item> {
var sections: [Section<Item>]

func numberOfSections() -> Int {
return sections.count
}

func numberOfItems(in section: Int) -> Int {
guard section < sections.count else { return 0 }
return sections[section].items.count
}

func item(at indexPath: IndexPath) -> Item {
return sections[indexPath.section].items[indexPath.row]
}
}

Table Config

列表配置基本包含三项:重用标识,注册cell,配置cell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//table config
protocol ConfiguratorType {
associatedtype Item
associatedtype Cell: UITableViewCell

func reuseIdentifier(for item: Item, indexPath: IndexPath) -> String
func configuredCell(for item: Item, tableView: UITableView, indexPath: IndexPath) -> Cell
func registerCells(in tableView: UITableView)
}

//构建配置器。泛型Item, Cell
struct Configurator<Item, Cell: UITableViewCell>: ConfiguratorType {
typealias CellConfigurator = (Cell, Item, UITableView, IndexPath) -> Cell

let configurator: CellConfigurator
let reuseIdentifier = "\(Cell.self)"

func reuseIdentifier(for item: Item, indexPath: IndexPath) -> String {
return reuseIdentifier
}

func configure(cell: Cell, item: Item, tableView: UITableView, indexPath: IndexPath) -> Cell {
return configurator(cell, item, tableView, indexPath)
}

func registerCells(in tableView: UITableView) {
if let path = Bundle.main.path(forResource: "\(Cell.self)", ofType: "nib"),
FileManager.default.fileExists(atPath: path) {
let nib = UINib(nibName: "\(Cell.self)", bundle: .main)
tableView.register(nib, forCellReuseIdentifier: reuseIdentifier)
} else {
tableView.register(Cell.self, forCellReuseIdentifier: reuseIdentifier)
}
}

func configuredCell(for item: Item, tableView: UITableView, indexPath: IndexPath) -> Cell {
let reuseIdentifier = self.reuseIdentifier(for: item, indexPath: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! Cell
return self.configure(cell: cell, item: item, tableView: tableView, indexPath: indexPath)
}
}

多类型cell配置器

假如我们的一个业务列表由两种cell组成:TableCell,NibCell。那么我们提供重用标识,配置cell时候就需要做好枚举判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
enum CellType {
typealias Model = String
typealias AnotherModel = String

case cell(Model)
case anotherCell(AnotherModel)
}

class TableCell: UITableViewCell {

}
class NibCell: UITableViewCell {

}

struct MoreCellsConfigurator: ConfiguratorType {
let cellConfigurator: Configurator<CellType.Model, TableCell>
let anotherCellConfigurator: Configurator<CellType.AnotherModel, NibCell>

func reuseIdentifier(for item: CellType, indexPath: IndexPath) -> String {
switch item {
case .cell:
return cellConfigurator.reuseIdentifier
case .anotherCell:
return anotherCellConfigurator.reuseIdentifier
}
}

func configuredCell(for item: CellType, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
switch item {
case .cell(let model):
return cellConfigurator.configuredCell(for: model, tableView: tableView, indexPath: indexPath)
case .anotherCell(let model):
return anotherCellConfigurator.configuredCell(for: model, tableView: tableView, indexPath: indexPath)
}
}

func registerCells(in tableView: UITableView) {
cellConfigurator.registerCells(in: tableView)
anotherCellConfigurator.registerCells(in: tableView)
}
}

使用多cells配置器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ExampleTableViewController<Item, Cell: UITableViewCell>: UITableViewController{

let dataSource: DataSource<Item>
let configurator: MoreCellsConfigurator

init(dataSource: DataSource<Item>, configurator: MoreCellsConfigurator) {
self.dataSource = dataSource
self.configurator = configurator
super.init(nibName: nil, bundle: nil)
configurator.registerCells(in: tableView)
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { fatalError() }

override func numberOfSections(in tableView: UITableView) -> Int {
return dataSource.numberOfSections()
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.numberOfItems(in: section)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = dataSource.item(at: indexPath) as! CellType

return configurator.configuredCell(for: item, tableView: tableView, indexPath: indexPath)
}
}

demo控制器举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
extension UIViewController {
func add(child: UIViewController, container: UIView, configure: (_ childView: UIView) -> Void = { _ in }) {
addChild(child)
container.addSubview(child.view)
configure(child.view)
child.didMove(toParent: self)
}
}

class GYViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
setupTable()
}

func setupTable() {
let section0 = Section<CellType>(items: [.cell("a"), .anotherCell("2"), .cell("c")])
let section1 = Section<CellType>(items: [.anotherCell("1"), .cell("b"), .anotherCell("3")])
let dataSource = DataSource(sections: [section0, section1])

//TableCell配置器
let configurator1 = Configurator { (cell, model: CellType.Model, tableView, indexPath) -> TableCell in
cell.textLabel?.text = model
return cell
}

//NibCell配置器
let configurator2 = Configurator { (cell, model: CellType.AnotherModel, tableView, indexPath) -> NibCell in
cell.textLabel?.text = model
return cell
}

//配置多种cell配置器
let aggregate = MoreCellsConfigurator(cellConfigurator: configurator1, anotherCellConfigurator: configurator2)

let table = ExampleTableViewController(dataSource: dataSource, configurator: aggregate)

add(child: table, container: view) { childView in
//布局约束
childView.frame = view.frame
}
}
}

评论