在一些购物类的APP中,我们经常会看到产品的瀑布流页面,这种瀑布流的展示可以通过UICollectionView来实现。本文就针对UICollectionView的用法做一个详细的介绍
简介
首先我们来看看官网上是怎么介绍UICollectionView的:
The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts.
UICollectionView可以用来管理一批有序的数据,并且可以使用自定义的layout来展示它们。
Collection view有着tableView类似的功能,将数据通过列表的形式展示出来,但是collection view更强大的功能在于它支持custom的layout,如多列、平铺、圆形布局等等,我们甚至可以动态的改变collection view的布局。如,我们可以任意的制定一个数据项在UI上的长度和宽度,也可以指定两个数据项之间的间隔大小。
Collection view是以Cell为单位来展示数据项的,然后cell也可以归类为section。例如Photo App, 数据项是一个image,collection view通过一个cell来展示这个image
Collection Views and Layout Objects
UICollectionView在初始化的时候需要指定一个layout,这个layout(UICollectionViewLayout类型的对象)用于布局所有的cell和 supplementary view. 这个layout只负责这些视图的显示位置和大小,并不管这些视图的创建。虽然我们初始化Collection view的时候指定了一个layout,但是这个layout是可以动态改变的,通过修改UICollectionView的collectionViewLayout属性来更新collection view的layout布局,一旦设置了这个值,collection view会马上更新视图展示。当然这个更新的过程是没有动画效果的,如果你想添加动画效果,可以调用collection view的方法:
setCollectionViewLayout(_ layout: UICollectionViewLayout,
animated animated: Bool,
completion completion: ((Bool) -> Void)?)
collection view中的layout尺寸是通过 UICollectionViewDelegateFlowLayout来实现的: ### 1. cell尺寸的设置
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
如果没有实现这个方法,则会使用flow layout的itemSize属性值作为cell的大小值。
另外,flow layout不会为了适应网格而裁剪cell的大小,如果一行展示不下,则会拉大cell之间的间隙,选择新的一行展示。
2. cell之间的间隔设置
这个间隔有2种:(下面以纵向的layout为例来说明)
- 横向cell之间的距离:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat
- 纵向row之间的距离
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat
注意: 这是minimum值的设置,在保证最小间隔的情况下去布局cell,如果一行排列不下,则换一行展示,从而上一行cell的间距也会被拉大。所以在程序中要固定这个间距就要动态的去计算cell的宽度(根据当前的collection view的宽度去计算)
3. supplementary view尺寸的设置
(1) header view
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
referenceSizeForHeaderInSection section: Int) -> CGSize
(2) footer view
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
referenceSizeForFooterInSection section: Int) -> CGSize
如果没有实现该方法,则会使用layout的 footerReferenceSize属性值和headerReferenceSize属性值来指定footer和header的尺寸大小,这两个属性的默认值为(0,0),如果返回值为(0,0)则没有footer或者header
注意: 在布局中,只有滚动方向的那个尺寸值才会被使用来决定view的尺寸。例如,对于纵向滚动的layout,layout只会使用返回size中的height值(view的宽度会使用collection view的宽度值),如果返回(100,0),则认为没有footer活着header
### 4. section的margins设置
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: leftMargin, bottom: 30, right: leftMargin)
}
如果没有实现这个方法,layout会使用它的sectionInset属性值来设置section的边距(这个属性值默认都是0)。
这个UIEdgeInsets值只对当前section的cell用,返回的UIEdgeInsets的4个值分别对应于:第一行cell和header之间的间距;左侧元素和collection view的左边框间的距离;最后一行元素和footer间的距离;右侧元素和collection view的右边框间的距离。
Collection View Datasource and Delegate
UICollectionView和UITableView类似,由UICollectionViewDataSource协议来提供数据源,复杂cells和supplementary view的创建,由UICollectionViewDelegate(UICollectionViewDelegateFlowLayout)负责Cell操作事件等的回调。
1. cell的创建
调用dequeueReusableCellWithReuseIdentifier:forIndexPath: 返回cell,然后对cell设置数据,最后返回给collection view;
注意: 在调用该方法前,需要注册这个cell的类: registerClass:forCellWithReuseIdentifier: 或者 registerNib:forCellWithReuseIdentifier:
2. supplementary view的创建
调用dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: , 返回辅助的view对象,配置它的内容,返回给collection view.
同样的,在调用该方法之前,需要注册这个view的类: registerClass(:forSupplementaryViewOfKind:withReuseIdentifier:) 或者 registerNib(:forSupplementaryViewOfKind:withReuseIdentifier:)
注意:针对不同类别的supplementary view都需要注册一次,
supplemetary view的类别有:
• UICollectionElementKindSectionHeader
• UICollectionElementKindSectionFooter
如果不需要返回这种类型的supplementary view,则不需要为这种类型dequeue reusable view, 如:
if kind == UICollectionElementKindSectionHeader {
let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: headerIdentifier, forIndexPath: indexPath)
...
return headerView
} else {
return UICollectionReusableView(frame: CGRectZero)
}
参考资料