三种方法实现
一、scrollView做垫子,上面添加多个tableView(不可取);
效率低下,cell不能循环利用
二、scrollView做滚动,每一项对应一个子控件imgview(过于复杂)
我们要做缓存机制;时刻监控控件在scrollView上的滚动位置移除或添加;每个控件都需要重新布局
三、采取UICollectionView,使用自定义布局设置cell显示(采用)
1、自定义流水布局中,指定滚动方向、默认列数、行间距、列间距、以及指定cell的大小itemSize
2、可以提供一个数组columnMaxYs(记录当前每一列的最大Y值),假如3列,我们就提供一个3个元素的数组,记录所有布局属性
columnMaxYs实现懒加载,
2.并在prepareLayout方法中 :
1.先将columnMaxYs清空。
2.再进行初始化每个元素为0.
3.获取所有的Cell的布局属性,而每一个Cell的布局属性通过调用layoutAttributesForItemAtIndexPath:方式获取,而调用该方法,就会执行第4步
(至于为什么在prepareLayout方法中初始化,而不是在init方法初始化:
是因为,该方法每次刷新都会调用,而init方法中只会在创建布局对象的时候只执行一次,
例如:如果进行下拉刷新最新数据的时候,需求重新初始化数据,而如果我们使用的是init方法的话,并不会调用也就并不会清除之前的然后初始化,
而使用该方法prepareLayout可以办到,因为它会再次调用,而init不会调用)
3.获取所有Cell的布局属性,
注意:对与所有Cell的布局属性,在第一次加载的时候需要计算一次,而当刷新collectionView的时候,当然也需要重新计算:所以我们提供一个布局属性的数组,来保存所有Cell的布局刷新,避免不必要的计算。
——> 放在哪? 计算最好呢?
———> 首选放在prepareLayout方法中,因为它会调用一次加载,而且当刷新的时候也会调用,满足需求,我们先将之前的全部移除,然后重新返回最新的布局属性数组; 但是,最好不要放在layoutAttributesForElementsInRect:方法(返回所有元素的布局属性数组中),因为改方法在滚动collectionView的时候,会频繁的调用,比较销毁性能。
3、在layoutAttributesForElementsInRect:方法(返回所有元素的布局属性数组 )
返回之前保存的所有Cell的布局属性数组
4、我们可以在layoutAttributesForItemAtIndexPath: 方法: 来调整 Cell的布局属性 , 指定Cell的 frame
1.在该方法中拿到当前cell的默认布局属性attrs,进行下面的调整,就可以实现瀑布流了
2.遍历columnMaxYs数组,需要找出最短一列的 列号 与 最大Y值
- 确定当前Cell的 存放的x.y位置 以及widht与height
—> width:根据屏幕宽度与列数以及每列的宽度求出. height:服务器返回的
—> x:需要根据当前最短一列的列号与Cell的宽度与间距可以求出来;y : 可以根据当前最短一列的最大Y值 + 行间距可以求出
4.重新设置修改当前Cell的布局属性attrs 的 frame即可。—> 之前已经拿到当前Cell的 默认布局属性,以及上一步已经获取到当前Cell需要存放的x.y位置后,
5.将修改Cell布局属性之后的,当前列当前Cell的Y值最大,所有我们要将该值记录到数组columnMaxYs中,以便下次对比
5、注意:我们需要设置collectionView的contentSize,它才会滚动,那么我们如何设置呢?
——> 在定义布局类中,系统提供了一个collectionViewContentSize的get对象方法(决定collectionView的contentSize)
—> contentSize的高度为:计算出最长那一列的最大Y值,也就是columnMaxYs的最大值 + 行间距
实现
1.创建一个控制器HXCollectionViewController,继承UICollectionViewController,使用我们自定义的流水布局HXWaterflowLayout(实现见其后)
#import "HXCollectionViewController.h"
#import "HXWaterflowLayout.h" // 自定义流水布局
@interface HXCollectionViewController ()
@end
@implementation HXCollectionViewController
static NSString * const reuseIdentifier = @"cellID";
- (void)viewDidLoad {
[super viewDidLoad];
// 切换布局
self.collectionView.collectionViewLayout = [[HXWaterflowLayout alloc] init];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor orangeColor];
return cell;
}
@end
2.自定义流水布局HXWaterflowLayout,继承UICollectionViewLayout
#import "HXWaterflowLayout.h"
#define HXCollectionW self.collectionView.frame.size.width
/** 每一行之间的间距 */
static const CGFloat HXDefaultRowMargin = 10;
/** 每一列之间的间距 */
static const CGFloat HXDefaultColumnMargin = 10;
/** 每一列之间的间距 top, left, bottom, right */
static const UIEdgeInsets HXDefaultInsets = {10, 10, 10, 10};
/** 默认的列数 */
static const int HXDefaultColumsCount = 3;
@interface HXWaterflowLayout()
/** 每一列的最大Y值 */
@property (nonatomic, strong) NSMutableArray *columnMaxYs;
/** 存放所有cell的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end
@implementation HXWaterflowLayout
#pragma mark - 懒加载
- (NSMutableArray *)columnMaxYs
{
if (!_columnMaxYs) {
_columnMaxYs = [[NSMutableArray alloc] init];
}
return _columnMaxYs;
}
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
_attrsArray = [[NSMutableArray alloc] init];
}
return _attrsArray;
}
#pragma mark - 实现内部的方法
/**
* 决定了collectionView的contentSize
*/
- (CGSize)collectionViewContentSize
{
// 找出最长那一列的最大Y值
CGFloat destMaxY = [self.columnMaxYs[0] doubleValue];
for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) {
// 取出第i列的最大Y值
CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue];
// 找出数组中的最大值
if (destMaxY < columnMaxY) {
destMaxY = columnMaxY;
}
}
return CGSizeMake(0, destMaxY + HXDefaultInsets.bottom);
}
- (void)prepareLayout
{
[super prepareLayout];
// 重置每一列的最大Y值
[self.columnMaxYs removeAllObjects];
for (NSUInteger i = 0; i<HXDefaultColumsCount; i++) {
[self.columnMaxYs addObject:@(HXDefaultInsets.top)];
}
// 计算所有cell的布局属性
[self.attrsArray removeAllObjects];
NSUInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSUInteger i = 0; i < count; ++i) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attrsArray addObject:attrs];
}
}
/**
* 说明所有元素(比如cell、补充控件、装饰控件)的布局属性
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
/**
* 说明cell的布局属性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
/** 计算indexPath位置cell的布局属性 */
// 水平方向上的总间距
CGFloat xMargin = HXDefaultInsets.left + HXDefaultInsets.right + (HXDefaultColumsCount - 1) * HXDefaultColumnMargin;
// cell的宽度
CGFloat w = (HXCollectionW - xMargin) / HXDefaultColumsCount;
// cell的高度,测试数据,随机数
CGFloat h = 50 + arc4random_uniform(150);
// 找出最短那一列的 列号 和 最大Y值
CGFloat destMaxY = [self.columnMaxYs[0] doubleValue];
NSUInteger destColumn = 0;
for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) {
// 取出第i列的最大Y值
CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue];
// 找出数组中的最小值
if (destMaxY > columnMaxY) {
destMaxY = columnMaxY;
destColumn = i;
}
}
// cell的x值
CGFloat x = HXDefaultInsets.left + destColumn * (w + HXDefaultColumnMargin);
// cell的y值
CGFloat y = destMaxY + HXDefaultRowMargin;
// cell的frame
attrs.frame = CGRectMake(x, y, w, h);
// 更新数组中的最大Y值
self.columnMaxYs[destColumn] = @(CGRectGetMaxY(attrs.frame));
return attrs;
}
@end