UICollectionViewFlowLayout是系统提供给我们一个封装好的流布局设置类,它的父类是UICollectionViewLayout,相对于父类它具有一些特别的属性。
下面依次讲解每个属性:
1、设置行与行之间的间距最小距离
@property (nonatomic) CGFloat minimumLineSpacing;
2、设置列与列之间的间距最小距离
@property (nonatomic) CGFloat minimumInteritemSpacing;
3、设置每个item的大小
@property (nonatomic) CGSize itemSize;
4、设置每个Item的估计大小,一般不需要设置
@property (nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0);
5、设置布局方向
@property (nonatomic) UICollectionViewScrollDirection scrollDirection;
6、设置头视图尺寸大小
@property (nonatomic) CGSize headerReferenceSize;
7、设置尾视图尺寸大小
@property (nonatomic) CGSize footerReferenceSize;
8、设置分区的EdgeInset
@property (nonatomic) UIEdgeInsets sectionInset;
9、下面这两个方法设置分区的头视图和尾视图是否始终固定在屏幕上边和下边
@property (nonatomic) BOOL sectionHeadersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);
@property (nonatomic) BOOL sectionFootersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);
看到这里我想大家也明白了,UICollectionViewFlowLayout相对于父类添加的属性就是更好地创建的表格视图。常用的有两种类型的 collection view 布局:
1.独立于内容的布局计算。这正是你所知道的像 UITableView 和 UICollectionViewFlowLayout 这些情况。每个 cell 的位置和外观不是基于其显示的内容,但所有 cell 的显示顺序是基于内容的顺序。可以把默认的 flow layout 做为例子。每个 cell 都基于前一个 cell 放置(或者如果没有足够的空间,则从下一行开始)。布局对象不必访问实际数据来计算布局,此时最方便的就是用系统封装好的UICollectionViewFlowLayout类了,当然你可以直接用,也可以继承它,自定义一些表格流。
2.基于内容的布局计算。布局对象直接访问 collection view 的数据源,根据数据源再指定相应的布局。在很多情况下,布局对象不仅需要取出当前可见 cell 的数据,还需要从所有记录中取出一些决定当前哪些 cell 可见的数据。所以如果有一个依赖内容的布局,那就是暗示你需要写自定义的布局类了,同时不能使用自定义的 UICollectionViewFlowLayout。
-----------------
好了,原理介绍完了下面讲解一下UICollectionViewFlowLayout用法和注意点(对于父类UICollectionViewFLayout的讲解需要等到下篇文章哦):
重写prepareLayout方法
- 作用:在这个方法中做一些初始化操作
- 注意:一定要调用[super prepareLayout]
重写layoutAttributesForElementsInRect:方法
- 作用:
- 这个方法的返回值是个数组
- 这个数组中存放的都是UICollectionViewLayoutAttributes对象
- UICollectionViewLayoutAttributes对象决定了cell的排布方式(frame等)
重写shouldInvalidateLayoutForBoundsChange:方法
- 作用:如果返回YES,那么collectionView显示的范围发生改变时,就会重新刷新布局
- 一旦重新刷新布局,就会按顺序调用下面的方法:
- prepareLayout
- layoutAttributesForElementsInRect:
重写targetContentOffsetForProposedContentOffset:withScrollingVelocity:方法
- 作用:返回值决定了collectionView停止滚动时最终的偏移量(contentOffset)
- 参数:
- proposedContentOffset:原本情况下,collectionView停止滚动时最终的偏移量
- velocity:滚动速率,通过这个参数可以了解滚动的方向
--------------------
不多说了直接上代码:
- (instancetype)init
{
if (self = [super init]) {
}
return self;
}
/**
* 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局
* 一旦重新刷新布局,就会重新调用下面的方法:
1.prepareLayout
2.layoutAttributesForElementsInRect:方法
*/
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
/**
* 用来做布局的初始化操作(不建议在init方法中进行布局的初始化操作)
*/
- (void)prepareLayout
{
[super prepareLayout];
// 水平滚动
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// 设置内边距
CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}
/**
UICollectionViewLayoutAttributes *attrs;
1.一个cell对应一个UICollectionViewLayoutAttributes对象
2.UICollectionViewLayoutAttributes对象决定了cell的frame
*/
/**
* 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性)
* 这个方法的返回值决定了rect范围内所有元素的排布(frame)
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// 获得super已经计算好的布局属性
NSArray *array = [super layoutAttributesForElementsInRect:rect];
// 计算collectionView最中心点的x值
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;
// 在原有布局属性的基础上,进行微调
for (UICollectionViewLayoutAttributes *attrs in array) {
// cell的中心点x 和 collectionView最中心点的x值 的间距
CGFloat delta = ABS(attrs.center.x - centerX);
// 根据间距值 计算 cell的缩放比例
CGFloat scale = 1 - delta / self.collectionView.frame.size.width;
// 设置缩放比例
attrs.transform = CGAffineTransformMakeScale(scale, scale);
}
return array;
}
/**
* 这个方法的返回值,就决定了collectionView停止滚动时的偏移量
*/
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
// 计算出最终显示的矩形框
CGRect rect;
rect.origin.y = 0;
rect.origin.x = proposedContentOffset.x;
rect.size = self.collectionView.frame.size;
// 获得super已经计算好的布局属性
NSArray *array = [super layoutAttributesForElementsInRect:rect];
// 计算collectionView最中心点的x值
CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
// 存放最小的间距值
CGFloat minDelta = MAXFLOAT;
for (UICollectionViewLayoutAttributes *attrs in array) {
if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
minDelta = attrs.center.x - centerX;
}
}
// 修改原有的偏移量
proposedContentOffset.x += minDelta;
return proposedContentOffset;
}