iOS使用CollectionView实现瀑布流

来源:互联网 发布:升级win10用什么软件 编辑:程序博客网 时间:2024/05/14 17:35

瀑布流的原理

将屏幕等分成三列 然后将图片加载到每一列中,在加入到列之前,首先判断那一列的高度最低,然后把图片加到高度最低的那列中。

瀑布流设计思路分析

使用UICollectionView,采用自定义布局的方式,设置cell的排列规则 完成瀑布流。

  1. 自定义布局中,指定滚动方向,默认列数,行间距,列间距,以及指定cell的大小itemSize
  2. 创建一个数组columnMaxYs(记录当前每一列的最大Y值)
  3. 通过判断记录的最大Y值是否为最小的一列 计算item的X坐标 和Y坐标 并记录添加上的item那一列的最大Y坐标 设置item的frame属性
  4. 当我们的item进入复用池的时候,我们的界面展示的我们眼睛看到的以及我们想的,是和事实不相符的。 当我们向上滑动的时候你感觉 Y值在无线变大,但其实我们的Y值在最上方还是0。这里就需要用到prepareLayout方法。这个方法会在item出现在屏幕上之前反复执行。
  5. 在第一次加载的时候我们会计算所有的item的布局属性,但是当我们上下滑动的时候,还是需要重新计算这些布局属性,所以我们需要提供一个布局属性数组存放Cell的布局属性,避免必要的计算。
  6. 我们需要知道如何返回我们计算出来的item的布局属性,并且在那里计算合适,不会出现数据丢失的现行, layoutAttributesForElementsInRect:(返回所有元素的布局属性数组)。
  7. 这里需要设置Collection的滚动属性 ,就需要设置她的ContentSize 。没关系系统已经给出了这样的方法collectionViewContentSize

瀑布流的基本实现代码

#import <UIKit/UIKit.h>@interface ZQCollectionViewController : UICollectionViewController@end
#import "ZQCollectionViewController.h"#import "ZQCollectionViewCell.h"#import "ZQCollectionViewLayout.h"@interface ZQCollectionViewController (){    NSMutableArray *arr;}@end@implementation ZQCollectionViewControllerstatic NSString * const reuseIdentifier = @"Cell";- (void)viewDidLoad {    [super viewDidLoad];    arr=[NSMutableArray array];    for (int i=0; i<49; i++) {        NSString *string=[NSString stringWithFormat:@"%d.jpg",i%5+1];        [arr addObject:string];    }    ZQCollectionViewLayout *layout=[[ZQCollectionViewLayout alloc]init];    layout.imageList=arr;    self.collectionView.collectionViewLayout=layout;    [self.collectionView registerNib:[UINib nibWithNibName:@"ZQCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:reuseIdentifier];}- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {    return 1;}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {    return arr.count;}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    ZQCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];    cell.imgV.image =[UIImage imageNamed:arr[indexPath.row]];    cell.backgroundColor = [UIColor purpleColor];    return cell;}@end
#import <UIKit/UIKit.h>@interface ZQCollectionViewCell : UICollectionViewCell//注意这里是使用XIB绘制的@property (weak, nonatomic) IBOutlet UIImageView *imgV;@end
#import "ZQCollectionViewCell.h"@implementation ZQCollectionViewCell@end
#import <UIKit/UIKit.h>@interface ZQCollectionViewLayout : UICollectionViewLayout@property(nonatomic,copy)NSArray *imageList;@end
#import "ZQCollectionViewLayout.h"#define NewDefaultCollectionViewWidth  self.collectionView.frame.size.width//static 只在当前作用域使用 const 不可修改的static const UIEdgeInsets NewDefaultInsets={10,10,10,10};//定义行列之间的间距static const CGFloat NewDefaultColumn=10;//定义默认的列数static int NewDeraultNumber=3;@interface ZQCollectionViewLayout ()//创建数组存放 Y值最大值 存放cell的布局属性@property(nonatomic,strong)NSMutableArray *columnArr;@property(nonatomic,strong)NSMutableArray *cellArr;@end@implementation ZQCollectionViewLayout- (NSMutableArray *)columnArr{    if (!_columnArr) {        _columnArr=[NSMutableArray array];    }    return _columnArr;}- (NSMutableArray *)cellArr{    if (!_cellArr) {        _cellArr =[NSMutableArray array];    }    return _cellArr;}- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];    //布局属性刷新更改    //获取总的横向间距    CGFloat xMARGIN=NewDefaultInsets.left+NewDefaultInsets.right+(NewDeraultNumber-1)*NewDefaultColumn;    CGFloat width=(NewDefaultCollectionViewWidth-xMARGIN)/NewDeraultNumber;#pragma mark -- 这里返回图片的高度    UIImage *image=[UIImage imageNamed:self.imageList[indexPath.row]];    CGFloat height=image.size.height *(width/image.size.width);#pragma mark -- 这里我们需要获取x坐标的值 如何获取 因为我们要做的是将需要展示的数组按顺序向下排列 而顺序就将后进来的插入到 最短的那一列 所以要获取这个x坐标我们就需要找出这个最小的y坐标才能确定    NSInteger sum=0;#pragma mark -- 下面这个遍历为什么要使用也是上面的原因 取出最大的y值 并获取对应的列数 其实columnArr只有三个元素    CGFloat sumMaxY=[self.columnArr[0] doubleValue];    for (int i=0; i<self.columnArr.count; i++) {        CGFloat anyMaxY=[self.columnArr[i]doubleValue];        if (sumMaxY>anyMaxY) {            sumMaxY=anyMaxY;            sum=i;        }    }    CGFloat x=NewDefaultInsets.left +sum*(width+NewDefaultColumn);    CGFloat y=NewDefaultInsets.top+sumMaxY;    attr.frame=CGRectMake(x, y, width, height);    //更新数组,获取最大的Y 下一次比较时用到  记住每一次都会走下面这个方法  而这个方法 第一次的时候都是0 第二次的时候两个0 第三次的时候就不一样了    self.columnArr[sum]=@(CGRectGetMaxY(attr.frame));    return attr;}#pragma mark -- 这里下面的方法 是当我们向上或向下滑动item的时候 我们需要将我们的最大Y坐标重置 为什么呢 因为如果你继续使用最大Y坐标 它还是向下排列- (void)prepareLayout{    [super prepareLayout];    //设置cell的最大Y值    [self.columnArr removeAllObjects];    for (int i=0; i<NewDeraultNumber; i++) {        //这里使用下面的方法是给最大值一个初始值        [self.columnArr addObject:@(NewDefaultInsets.top)];    }    //设置cell的布局属性  这里的self.layoutAttributesForItemAtIndexPath 是本类的一个属性 通过对应的indexPath我们可以拿到对应的item的布局属性 然后存储起来    [self.cellArr removeAllObjects];    NSInteger count=[self.collectionView numberOfItemsInSection:0];    for (int i=0; i<count; i++) {        NSIndexPath *indexPath=[NSIndexPath indexPathForItem:i inSection:0];        UICollectionViewLayoutAttributes *attrs=[self layoutAttributesForItemAtIndexPath:indexPath];        [self.cellArr addObject:attrs];    }}#pragma mark -- 设置collectionView的范围 contentSize- (CGSize)collectionViewContentSize{    CGFloat sumMaxY=[self.columnArr[0] doubleValue];    for (int i=0; i<self.columnArr.count; i++) {        CGFloat anyMaxY=[self.columnArr[i]doubleValue];        if (sumMaxY>anyMaxY) {            sumMaxY=anyMaxY;        }    }    //这里返回的横坐标是什么都可以    return CGSizeMake(0, sumMaxY);}- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{    //在里我们将我们存储起来的item布局属性交付给cell    return self.cellArr;}@end
0 0