一、UICollectionView介绍
UICollectionView是用来展示一系列数据的视图,相较于UITableView,其允许我们实现自定义的排版布局,例如实现画廊、瀑布流、横向Banner等效果。
二、UICollectionView的使用Demo1
UICollectionView的使用方法也类似UITableView,我们只需通过数据源UICollectionViewDatasource提供显示的数据,通过实现UICollectionViewDelegate的代理方法处理点击事件等。
下面我们来创建个简单的动物世界Demo,最终效果如图:
先新建cell,继承自UICollectionViewCell,并在cell中添加一个UIImageView
先贴出UICollectionView的完整代码,再做详细讲解,代码如下:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
view.addSubview(collectionView)
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCell
cell.imageView.image = UIImage(named: "\(indexPath.row)")
return cell
}
}
下面我们详细介绍其中的方法:
1、Layout
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
Layout是UICollectionView的核心,其定义了每个Cell、头视图、尾视图在UICollectionView中的布局(尺寸和位置),正常需通过创建UICollectionViewLayout的子类,实现对应方法。但如果只是想实现简单的水平或垂直的网格排布,可以像例子中直接用苹果以及为我们准备好的UICollectionViewFlowLayout即可。例子中将每个cell的大小设置为100*100,
1、Cell
collectionView.dataSource = self
collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
UICollectionView的每一个数据,对应着一个UICollectionViewCell,与UITableview相同,为了减少系统资源开销,苹果也是使用了重用机制,避免大量创建视图,但不同于UITableView可以在cellforRow方法中手动创建UITableViewCell,在UICollectionView中只能预先通过以下了两种方法注册cell:
func register(AnyClass?, forSupplementaryViewOfKind: String, withReuseIdentifier: String)
func register(UINib?, forCellWithReuseIdentifier: String)
3、DataSource
当UICollectionView被初次加载时,会请求数据源获取当前可见的Cell和额外视图,在对应的数据源方法中通过dequeueReusableCell方法从可重用池中获取之前已预先注册的cell:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCell
cell.imageView.image = UIImage(named: "\(indexPath.row)")
return cell
}
三、Supplementary view
cell还可以分为成多个组(section),每个组可以设置头视图(section header)和尾视图(section footer),头视图和尾视图统称为额外视图(supplementary view),如下是苹果官方示例图
现在我们也为动物世界Demo添加上头视图,显示分组,最终效果如图:
先新建ZoneCollectionReusableView,继承自UICollectionReusableView,并在ZoneCollectionReusableView中添加一个UILabel和UIView分别显示标题和分割线
照例先上代码,这次代码是在前面的基础上添加,只列出新增代码及其前一行
class ViewController: UIViewController {
override func viewDidLoad() {
...
layout.itemSize = CGSize(width: 100, height: 100)
layout.headerReferenceSize = CGSize(width: view.bounds.size.width, height: 44)
...
collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
collectionView.register(UINib(nibName: "ZoneCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ZoneCollectionReusableView")
...
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ZoneCollectionReusableView", for: indexPath) as! ZoneCollectionReusableView
header.nameLabel.text = "第\(indexPath.section+1)组"
return header
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
}
layout.headerReferenceSize = CGSize(width: view.bounds.size.width, height: 44)
同理,由layout设置header view的大小布局,更多可设置属性如下:
@property(nonatomic) UICollectionViewScrollDirection scrollDirection
grid的滑动方向
@property(nonatomic) CGFloat minimumLineSpacing
每行之间最小的距离
@property(nonatomic) CGFloat minimumInteritemSpacing
每一行中每个item间的最小的距离
@property(nonatomic) CGSize itemSize
每一个items的默认大小
@property(nonatomic) CGSize estimatedItemSize
每一个items的估计大小(iOS8之后的属性,只要设置了estimatedItemSize,collection view 就会根据 cell 里面的 autolayout 约束去确定cell 的大小)
@property(nonatomic) UIEdgeInsets sectionInset
section里面内容的页边空白
@property(nonatomic) CGSize headerReferenceSize
section头部的默认大小
@property(nonatomic) CGSize footerReferenceSizesection
section尾部的默认大小
collectionView.register(UINib(nibName: "ZoneCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ZoneCollectionReusableView")
预先注册视图,以便请求数据源方法时,通过dequeueReusableSupplementaryView方法返回重用视图。
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
数据源方法中,numberOfSections返回3表示有3个分组,每个分组有6个cell
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ZoneCollectionReusableView", for: indexPath) as! ZoneCollectionReusableView
header.nameLabel.text = "第\(indexPath.section+1)组"
return header
}
以上方法通过dequeueReusableSupplementaryView获取之前注册的视图并返回。
四、更多
这篇只是开篇,接下来的文章将介绍UICollectionView的进阶用法,如实现瀑布流、Banner、画廊等。