如图所示的多表视图是一个很常用的东西,之前我是用UIScrollView
和UITableViewController
做的。把当前的控制器作为一个父控制器,添加三个UITableViewController
的实例作为子控制器,把父控制器中的 scrollView 作为容器,然后添加子控制器中的 tableView 作为子视图。这样做有一个问题,一旦有十几二十个表的话,内存就要爆炸了。解决的办法是可以自己写个重用机制,不过这显然没必要,用自带重用机制的UICollectionView
应该是个更好的选择。
首先新建个HomeContainerViewController
,继承自UICollectionViewController
,然后在viewDidLoad
里面加上这两句:
collectionView?.pagingEnabled = true
collectionView?.bounces = false
这样滑动的时候就会有翻页的段落感,滑到边界的时候也不会有回弹效果。
然后要用 layout 控制布局,用最常用的 UICollectionViewFlowLayout
就行了,设置单元格的宽高,既然是翻页,宽肯定是跟屏幕等宽,高度就看你需求了,但是不要超过 collectionView 的高,如下:
lazy var layout: UICollectionViewFlowLayout = {
let lazyLayout = UICollectionViewFlowLayout()
lazyLayout.itemSize = CGSize(width: Width, height: Height)
lazyLayout.minimumInteritemSpacing = 0
lazyLayout.minimumLineSpacing = 0
lazyLayout.scrollDirection = .Horizontal
return lazyLayout
}()
之后就可以用这个 layout 来初始化之前定义的HomeContainerViewController
。接下来我们要自定义一个UICollectionViewCell
,让它包含一个 tableView:
class HomeCollectionViewCell: UICollectionViewCell {
var tableView: UITableView!
var dataSource: HomeTableDataSource!
override init(frame: CGRect) {
super.init(frame: frame)
tableView = UITableView(frame: bounds, style: .Grouped)
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: CellReuseIdentifier.LatestArticles)
addSubview(tableView)
dataSource = HomeTableDataSource()
tableView.dataSource = dataSource
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这边还有一个 dataSource
(同理可自行添加 delegate),是 tableView 的数据源,可能大部分人习惯把控制器又当 dataSource 又当 delegate,不过我比较喜欢分开,就算是用同一个控制器,也会用extension
把代码分开。好现在我们看看如何定义这个 dataSource:
class HomeTableDataSource: NSObject, UITableViewDataSource {
var cellData: String?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 20
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CellReuseIdentifier.LatestArticles, forIndexPath: indexPath)
//Configure the cell...
cell.textLabel?.text = cellData
return cell
}
}
注意一定要继承 NSObject
,因为 UITableViewDataSource
协议是继承了NSObjectProtocol
协议的,所以如果你不继承NSObject
的话,还得自己写一堆方法来遵守NSObjectProtocol
协议。因为这边只是个 Demo,所以我直接在 cell 中显示cellData
的值,那cellData
的值在哪里设置呢?显然是在HomeContainerViewController
中:
let tableViewDataList = ["first table", "second table", "third table"]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! HomeCollectionViewCell
// Configure the cell
cell.dataSource.cellData = tableViewDataList[indexPath.section]
cell.tableView.reloadData()
return cell
}
在真实场景中一般是会在 dataSource 中放一个 urlString
的属性,然后一旦这个属性被赋值就自动联网取数据。这边 cell 是会被复用的,在翻到第三页时,会复用第一页的 cell ,第四页复用第二页的 cell……依此类推,所以需要给 cell 中的tableView
调用 reloadData
方法,不然就算改变了表中的数据,也不能正确的显示(奇数页都显示第一页的数据,偶数页都显示第二页的数据)。
这样就完成了一个多表视图,实际项目一般会在 table 上方放个小滑块指示器什么的,也很简单,只要在cellForItemAtIndexPath
方法中根据indexPath.section
来设置滑块位置就好了。