您的位置:首页 > 移动开发 > IOS开发

iOS使用CollectionView实现瀑布流

2016-03-16 09:27 351 查看

瀑布流的原理

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

瀑布流设计思路分析

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

自定义布局中,指定滚动方向,默认列数,行间距,列间距,以及指定cell的大小itemSize

创建一个数组columnMaxYs(记录当前每一列的最大Y值)

通过判断记录的最大Y值是否为最小的一列 计算item的X坐标 和Y坐标 并记录添加上的item那一列的最大Y坐标 设置item的frame属性

当我们的item进入复用池的时候,我们的界面展示的我们眼睛看到的以及我们想的,是和事实不相符的。 当我们向上滑动的时候你感觉 Y值在无线变大,但其实我们的Y值在最上方还是0。这里就需要用到prepareLayout方法。这个方法会在item出现在屏幕上之前反复执行。

在第一次加载的时候我们会计算所有的item的布局属性,但是当我们上下滑动的时候,还是需要重新计算这些布局属性,所以我们需要提供一个布局属性数组存放Cell的布局属性,避免必要的计算。

我们需要知道如何返回我们计算出来的item的布局属性,并且在那里计算合适,不会出现数据丢失的现行, layoutAttributesForElementsInRect:(返回所有元素的布局属性数组)。

这里需要设置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 ZQCollectionViewController

static 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: