本文主要介绍在MVC模式下,拆分TableView的代理,实现不同数据源动态创建不同的Cell。
文件结构如下:
文件结构解析
Controllers
这里放的是页面的Controller,主要负责逻辑加载和设置TableView的代理
Models
- Items
一个item对应一个Cell,对数据的单元封装 - DataSource
自定义的DataSource子类,用于实现TableView的UITableViewDataSource代理,并且分发item与Cell的对应关系
model主要接收数据,封装成item,并加入数组
Views
- Cells
自定义与item一一对应的Cell
重点解析
TableView有两个代理,分别是是UITableViewDelegate和UITableViewDataSource,这里需要重点针对UITableViewDataSource进行一些定制。
LittleHTableViewDataSource
首先我们自己定义一个协议LittleHTableViewDataSourcePotocol具体如下:
public protocol LittleHTableViewDataSourcePotocol:NSObjectProtocol,UITableViewDataSource
{
func tableView(_ tableView:UITableView, objectForRowAt indexPath:IndexPath) -> AnyObject?
func tableView(_ tableView: UITableView, cellClassForObject object: AnyObject) -> LittleHTableViewBaseCell.Type
func tableView(_ tableView: UITableView, indexPathForObject object: AnyObject) -> IndexPath?
func tableView(_ tableView: UITableView, cell:UITableViewCell, willAppearAtIndexPath indexPath: IndexPath) -> Void
}
然后我们声明一个自定义的DataSource类,实现LittleHTableViewDataSourcePotocol协议
public class LittleHTableViewDataSource: NSObject,LittleHTableViewDataSourcePotocol {
public var items : NSArray
public init(items : NSArray) {
self.items = items
}
// MARK:
// MARK: -LittleHTableViewDataSourcePotocol 方法
public func tableView(_ tableView: UITableView, objectForRowAt indexPath: IndexPath) -> AnyObject? {
if indexPath.row < self.items.count {
return self.items.object(at: indexPath.row) as AnyObject?
} else {
return nil
}
}
public func tableView(_ tableView: UITableView, cellClassForObject object: AnyObject) -> LittleHTableViewBaseCell.Type {
return LittleHTableViewBaseCell.self
}
public func tableView(_ tableView: UITableView, indexPathForObject object: AnyObject) -> IndexPath? {
return nil
}
public func tableView(_ tableView: UITableView, cell:UITableViewCell, willAppearAtIndexPath indexPath: IndexPath) -> Void {
}
// MARK:
// MARK: -UITableViewDataSource方法
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object:AnyObject? = self.tableView(tableView, objectForRowAt: indexPath)
var cell1:UITableViewCell
if let obj1 = object {
let cellClass:LittleHTableViewBaseCell.Type = self.tableView(tableView, cellClassForObject: obj1)
let identifier:String = cellClass.LittleHIdentifier()
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: identifier)
if cell == nil {
cell = cellClass.init(style: UITableViewCellStyle.default, reuseIdentifier: identifier)
}
cell1 = cell!
} else {
cell1 = LittleHTableViewBaseCell(style: UITableViewCellStyle.default, reuseIdentifier: nil)
}
if cell1 is LittleHTableViewBaseCell {
let cell2 = cell1 as! LittleHTableViewBaseCell
cell2.setObject(object)
}
return cell1
}
public func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
if let view1 = tableView.tableHeaderView {
if index == 0 {
tableView.scrollRectToVisible(view1.bounds, animated: false)
return -1
}
}
// let index2 = advance(title.startIndex, 1)
var index2:String.Index = title.startIndex
index2 = title.index(index2, offsetBy: 1)
let letter:String = title.substring(to: index2)
let sectionCount:Int = tableView.numberOfSections
var i:Int = 0
for _ in 1...sectionCount {
let section :String = (tableView.dataSource?.tableView!(tableView, titleForHeaderInSection: i))!
if section.hasPrefix(letter) {
return i
}
i += 1
}
if index > sectionCount {
return sectionCount - 1
} else {
return index
}
}
}
在我们自己定义的这个DataSource里面可以看到已经实现了UITableViewDataSource,我们通过LittleHTableViewDataSourcePotocol协议的
func tableView(_ tableView: UITableView, cellClassForObject object: AnyObject) -> LittleHTableViewBaseCell.Type
方法获取到具体Cell的classType,然后创建这个类型的Cell,并且通过LittleHTableViewDataSourcePotocol协议的
func tableView(_ tableView:UITableView, objectForRowAt indexPath:IndexPath) -> AnyObject?
方法获取Cell对应的数据,在Cell的setObject方法中进行数据赋值。
Controller关键代码
在我们使用到tableView的地方,将tableView的UITableViewDataSource代理设置为这个类的对象。如:
public var dataSource : LittleHTableViewDataSource?{
get{
return _dataSource
}
set{
if newValue != _dataSource {
_dataSource = newValue
}
if let tableView1 = self.tableView {
tableView1.dataSource = _dataSource
tableView1.reloadData()
}
}
}
我们希望Cell的高度能在Cell内部返回,最好在item数据封装的时候就直接计算好Cell的高度,然后直接返回,实现UITableViewDelegate的下面方法将返回的高度的任务转移到Cell
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (tableView.dataSource is LittleHTableViewDataSource) {
let dataSource:LittleHTableViewDataSource? = tableView.dataSource as? LittleHTableViewDataSource
if let d1 = dataSource {
let object = d1.tableView(tableView, objectForRowAt: indexPath)
if let obj1 = object {
let t1:LittleHTableViewBaseCell.Type = d1.tableView(tableView, cellClassForObject: obj1)
return t1.tableView(tableView, rowHeightForObject: obj1)
}
}
}
return 44.0
}
Cell
cell上需要实现
public func setObject (_ obj:AnyObject?) -> Void
来获取数据
实现
public class func tableView(_ tableView: UITableView, rowHeightForObject object: AnyObject) -> CGFloat
获取高度
实现
public class func LittleHIdentifier() -> String
设置reuseIdentifier
主要思路,将TableView的UITableViewDataSource的Cell构建能够根据classType去动态构建,把数据类型item和Cell一一对应。
demo地址