瀑布流基本落成,瀑布流的落到实处

一、瀑布流应用方案

亚州城ca88手机版 1不可取.png亚州城ca88手机版 2过于复杂.png亚州城ca88手机版 3最优方案.png

一、瀑布流施工方案

两种方式完成

在上一篇小说中,为我们介绍了UICollectionView的主干使用格局,进程和故事情节都相比较简单,有野趣看的同桌能够点击这里,此番将为大家介绍的是什么利用UICollectionLayout自定义复杂的布局。

二、瀑布流设计思路深入分析

  • 1、自定义流水布局中,钦点滚动方向、暗中认可列数、行间距、列间距、以及内定cell的大小itemSize
  • 2、能够提供贰个数组column马克斯Ys(记录当前每一列的最大Y值),要是3列,我们就提供三个3个成分的数组,记录全体布局属性

      1. column马克斯Ys完成懒加载,
    • 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值 3. 确定当前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值,也便是column马克斯Ys的最大值

  • 行间距“`

亚州城ca88手机版 4效果图.png

  • 1.创立二个调节器JPCollectionViewController,承接UICollectionViewController,使用我们自定义的湍流布局JPWaterflowLayout

#import "JPCollectionViewController.h"#import "JPWaterflowLayout.h" // 自定义流水布局@interface JPCollectionViewController ()@end@implementation JPCollectionViewControllerstatic NSString * const reuseIdentifier = @"cellID";- viewDidLoad { [super viewDidLoad]; // 切换布局 self.collectionView.collectionViewLayout = [[JPWaterflowLayout 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.自定义流水布局JPWaterflowLayout,承接UICollectionViewLayout

#import "JPWaterflowLayout.h"#define JPCollectionW self.collectionView.frame.size.width/** 每一行之间的间距 */static const CGFloat JPDefaultRowMargin = 10;/** 每一列之间的间距 */static const CGFloat JPDefaultColumnMargin = 10;/** 每一列之间的间距 top, left, bottom, right */static const UIEdgeInsets JPDefaultInsets = {10, 10, 10, 10};/** 默认的列数 */static const int JPDefaultColumsCount = 3;@interface JPWaterflowLayout()/** 每一列的最大Y值 */@property (nonatomic, strong) NSMutableArray *columnMaxYs;/** 存放所有cell的布局属性 */@property (nonatomic, strong) NSMutableArray *attrsArray;@end@implementation JPWaterflowLayout#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 */- 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 + JPDefaultInsets.bottom);}- prepareLayout{ [super prepareLayout]; // 重置每一列的最大Y值 [self.columnMaxYs removeAllObjects]; for (NSUInteger i = 0; i<JPDefaultColumsCount; i++) { [self.columnMaxYs addObject:@(JPDefaultInsets.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:rect{ return self.attrsArray;}/** * 说明cell的布局属性 */- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; /** 计算indexPath位置cell的布局属性 */ // 水平方向上的总间距 CGFloat xMargin = JPDefaultInsets.left + JPDefaultInsets.right + (JPDefaultColumsCount - 1) * JPDefaultColumnMargin; // cell的宽度 CGFloat w = (JPCollectionW - xMargin) / JPDefaultColumsCount; // cell的高度,测试数据,随机数 CGFloat h = 50 + arc4random_uniform; // 找出最短那一列的 列号 和 最大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 = JPDefaultInsets.left + destColumn * (w + JPDefaultColumnMargin); // cell的y值 CGFloat y = destMaxY + JPDefaultRowMargin; // cell的frame attrs.frame = CGRectMake(x, y, w, h); // 更新数组中的最大Y值 self.columnMaxYs[destColumn] = @(CGRectGetMaxY(attrs.frame)); return attrs;}@end

亚州城ca88手机版 5

一、scrollView做垫子,下边增加三个tableView(不可取);

频率低下,cell不能够循环利用

1、UICollectionViewLayout简介

不可取.png

二、scrollView做滚动,每一类对应二个子控件imgview(过于复杂)

咱俩要做缓存机制;时刻监督控件在scrollView上的滚动地点移除或加上;每一个控件都亟需再行布局

中央方法

在UICollectionViewLayout时,大家入眼会重写它的以下多少个主意

- prepareLayout;

prepareLayout会在四个机缘调用,第4回开头化layout的时候,刷新layout的时候以及艺术- shouldInvalidateLayoutForBoundsChange:newBounds返回YES的时候

- collectionViewContentSize;

该办法重返collectionView的故事情节的深浅

- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:rect; 

该方法会再次回到rect范围内有所cell的布局属性UICollectionViewLayoutAttributes

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;

该格局再次回到对应indexPath下的cell的布局属性UICollectionViewLayoutAttributes

  • 瀑布流基本落成,瀑布流的落到实处。shouldInvalidateLayoutForBoundsChange:newBounds;

该方法返回在界面发生变化是是否要重新布局,返回YES则会重新布局
  • targetContentOffsetForProposedContentOffset:proposedContentOffset;

返回滑动后的collectonView的偏移量,默认返回proposedContentOffset参数的值,在这里我们可以手动设置实际需要的偏移量#####UICollectionViewLayout与UICollectionViewFlowLayout在此之前,我们先来简单的关注一下`UICollectionViewFlowLayout`和`UICollectionViewLayout`的关系:`UICollectionViewFlowLayout`是系统为我们封装的一个继承于`UICollectionViewLayout`的子类,系统已经写好了布局,所以如果我们在```- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:rect; ```方法中调用 ``` NSArray *attributesArr = [super layoutAttributesForElementsInRect:rect];```,可以得到系统为我们写好的布局,但是如果直接继承于UICollectionViewLayout,上述方法得不到任何布局,所以我们必须要重写`- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;`方法,在方法中写好布局并调用,这样才能为cell布局。#####UICollectionViewLayoutAttributes关于cell的布局,我们还需要着重看一个类:UICollectionViewLayoutAttributes,它就是我们上面一直所说的cell的布局类,cell所有的布局属性都是要写到该类中的,那它到底都有哪些属性呢:

@property (nonatomic) CGRect frame;@property (nonatomic) CGPoint
center;@property (nonatomic) CGSize size;@property (nonatomic)
CATransform3D transform3D;@property (nonatomic) CGRect bounds
);@property (nonatomic) CGAffineTransform transform ;@property
(nonatomic) CGFloat alpha;@property (nonatomic) NSInteger zIndex; //
default is 0@property (nonatomic, getter=isHidden) BOOL hidden;@property
(nonatomic, strong) NSIndexPath *indexPath;

改变了这些属性,并传递给layout,就可以改变cell的布局,所以归根到底,不管多复杂的布局,都是在改变这些属性。###自定义UICollectionViewLayout具体实现下面,我们就在具体的实例中看一下,如果使用自定义layout:####先为大家贴出代码:######创建一个继承于UICollectionViewLayout的子类>.h

@interface My_1Layout : UICollectionViewLayout@end

>.m

@interface My_1Layout(){UIEdgeInsets _edgeInset;//内边距CGFloat
_lineSpacing;//行间距CGFloat _columnsSpacing;//列间距NSInteger
_columnsNum;//列数NSMutableArray
*_columnsHeightArray;//用来存放全体列的万丈CGFloat
_maxHeight;//collectionContent最大惊人}@property (nonatomic,strong)
NSMutableArray *attributesArray;//用来寄存全部的cell的布局

@end

@implementation My_1Layout

  • (instancetype)init{if ([super init]) {_edgeInset =
    UIEdgeInsetsMake(5, 10, 5, 10);_lineSpacing = 10;_columnsSpacing =
    10;_columnsNum = 3;_maxHeight =
    _edgeInset.top;_columnsHeightArray = [NSMutableArray
    new];_columnsHeightArray = [NSMutableArray
    arrayWithCapacity:_columnsNum];}return self;}

  • prepareLayout{/**记住,一定要先调用父类的prepareLayout*/[super
    prepareLayout];

    [_columnsHeightArray removeAllObjects];for (int i = 0; i <
    _columnsNum ; i ++) {[_columnsHeightArray addObject:[NSNumber
    numberWithInteger:_edgeInset.top]]瀑布流基本落成,瀑布流的落到实处。;}

    [self.attributesArray
    removeAllObjects];/**调用layoutAttributesForItemAtIndexPath:方法,根据collectionView中cell的个数,使用for循环,创制对应个数的cell的attributes,并寄存到_columnsHeightArray数组中(也足以将该进度置于layoutAttributesForElementsInRect:中去实行)/NSInteger
    cellNum = [self.collectionView numberOfItemsInSection:0];for (int
    i = 0; i < cellNum; i ++) {NSIndexPath
    indexPath=[NSIndexPath
    indexPathForItem:i inSection:0];UICollectionViewLayoutAttributes
    *attri = [self
    layoutAttributesForItemAtIndexPath:indexPath];[self.attributesArray
    addObject:attri];}}-shouldInvalidateLayoutForBoundsChange:newBounds{

    return YES;}-(NSArray<UICollectionViewLayoutAttributes >)layoutAttributesForElementsInRect:rect{/*间接再次来到从前寄存好的持有cell的attributes(也得以将prepareLayout方法中for循环创造attributes的长河置于这里进行)/return
    self.attributesArray;}-(UICollectionViewLayoutAttributes)layoutAttributesForItemAtIndexPath:(NSIndexPath)indexPath{UICollectionViewLayoutAttributes*attributes=[UICollectionViewLayoutAttributes
    layoutAttributesForCellWithIndexPath:indexPath];

    CGFloat cellW =
    (kScreenWidth-_edgeInset.left-_edgeInset.right-(_columnsNum-1)*_columnsSpacing)/_columnsNum;CGFloat
    cellH = indexPath.item%2==0?160:125;// 应该增多cell的列号NSInteger
    minHeightColumn = 0;// 应该加多cell的列的惊人CGFloat minColumnHeight
    = [_columnsHeightArray[minHeightColumn] doubleValue];//循环
    获得最小的列的万丈和该列的列号for (int i = 1; i <
    _columnsHeightArray.count; i ++ ) {CGFloat tempH =
    [_columnsHeightArray[i] floatValue];if (minColumnHeight >
    tempH) {minColumnHeight = tempH;minHeightColumn =
    i;}}//为高度最小的列加多cellCGFloat cellY =
    [_columnsHeightArray[minHeightColumn]
    floatValue]+_lineSpacing;CGFloat cellX = _edgeInset.left +
    minHeightColumn * (cellW + _columnsSpacing);attributes.frame =
    CGRectMake(cellX, cellY, cellW, cellH);//保存最新的可观CGFloat
    newHeight = cellY+cellH;[_columnsHeightArray
    replaceObjectAtIndex:minHeightColumn withObject:[NSNumber
    numberWithInteger:newHeight]];//重返布局新闻return attributes;}

  • collectionViewContentSize{//依据最高的列
    设置collectionContentSize[_columnsHeightArray
    enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx,
    BOOL * _Nonnull stop) {CGFloat maxHeight =
    [_columnsHeightArray[idx]floatValue];if (maxHeight >
    _maxHeight) {_maxHeight = maxHeight;}}];return
    CGSizeMake(kScreenWidth, _maxHeight);}

  • (NSMutableArray *)attributesArray{if (!_attributesArray)
    {_attributesArray = [NSMutableArray new];}return
    _attributesArray;}

大概思路就是:首先初始化layout的各种属性和变量,在- prepareLayout中循环调用-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:方法,为所有的cell添加布局,最后从-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:中将其返回即可。本例中,调用layout的方法也很简单,就正常创建layout,并赋给collectionView就可以了:

My_1Layout layout = [[My_1Layout alloc]init];UICollectionView
collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0,
64, kScreenWidth, kScreenHeight-64) collectionViewLayout:layout];

下面是效果图:![效果图-1.gif](http://upload-images.jianshu.io/upload_images/1914621-6c873fb7927b595e.gif?imageMogr2/auto-orient/strip)

(注:在支付中,对于要求在表面包车型大巴调控器中装置layout属性的,包罗内边距、行间距、列间距、列数以及cell的起来大小等,可以为layout增加代理,使用代理方法再次来到)。

下面我们再看一个例子:首先,我们先创建一个继承于UICollectionViewFlowLayout的layout子类,layout类中不做任何实现,然后在控制器中赋值给collectionView,控制器中关于collectionView和数据源的设置和上例一样,然后运行程序,查看效果:![效果图-2.PNG](http://upload-images.jianshu.io/upload_images/1914621-315258ac335f087d.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)我们发现,尽管layout没有做任何布局,但是collectionView任然可以显示,这就说明,UICollectionViewFlowLayout已经为我们做好了一个布局,就是我们现在看到的流水布局,所以,对于继承于UICollectionViewFlowLayout的 类,如果要改变cell的布局,只需要获取系统默认为cell写好的布局,然后再此基础上进行修改就可以了。那么怎样获取UICollectionViewFlowLayout为我们写好的布局呢,使用父类调用-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:。废话不多说,直接上代码:

/***prepareLayout会频仍调用,所以只做一些轻松易行的开首化操作*/

  • prepareLayout{[super prepareLayout];//
    水平滚动self.scrollDirection =
    UICollectionViewScrollDirectionHorizontal;//设置cell大小(可停放外面包车型客车操纵器中)self.itemSize
    = CGSizeMake;//设置内边距CGFloat inset =
    (self.collectionView.frame.size.width – self.itemSize.width) *
    0.5;self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);

}/***安装为YES,collectionView的显得范围发生变化时,将要刷新布局*/-shouldInvalidateLayoutForBoundsChange:newBounds{

return YES;

}/***归来全部cell的布局属性*/-(NSArray<UICollectionViewLayoutAttributes
*>
*)layoutAttributesForElementsInRect:rect{//获取UICollectionViweFlowLayout已经做好的布局NSArray
attrbutesArray = [super
layoutAttributesForElementsInRect:rect];//计算collectionView可视范围的骨干点所对应的collectionView的x值CGFloat
centerX = self.collectionView.contentOffset.x +
self.collectionView.frame.size.width \

0.5;//以每一种cell中央点到centerX的偏离为仿照效法,对cell进行缩放for
(UICollectionViewLayoutAttributes *attributes in attrbutesArray)
{//cell的中坚点到centerX的偏离CGFloat distance = ABS(attributes.center.x

  • centerX);//依照distance总计cell的缩放比例CGFloat scale = 1 – (distance
    /
    self.collectionView.frame.size.width);//设置缩放比例attributes.transform3D
    = CATransform3DMakeScale(scale, scale, scale);}//
    重临调节现在的布局属性数组return attrbutesArray;}/**

  • 在再度刷新布局时调用*/

  • (UICollectionViewLayoutAttributes
    *)layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath
    {UICollectionViewLayoutAttributes
    attr =
    [UICollectionViewLayoutAttributes
    layoutAttributesForCellWithIndexPath:indexPath];return attr;}/**

  • collectionView结束滑动时调用,能够手动设置collectionView的偏移量
  • proposedContentOffset collectionView原来的偏移量*/

  • targetContentOffsetForProposedContentOffset:proposedContentOffset
    withScrollingVelocity:velocity {//计算出最后展现的矩形框CGRect
    endRect;endRect.origin.x = proposedContentOffset.x;endRect.origin.y
    = 0;endRect.size = CGSizeMake(self.collectionView.frame.size.width,
    self.collectionView.frame.size.height);

    //得到全体cell的布局属性NSArray *attributesArr = [super
    layoutAttributesForElementsInRect:endRect];

    //计算collectionView最中央点的x值CGFloat centerX =
    proposedContentOffset.x + self.collectionView.frame.size.width *
    0.5;//循环全体的布局属性,获得距离宗旨点近年来的cell到宗旨点的偏离CGFloat
    minDelta = MAXFLOAT;for (UICollectionViewLayoutAttributes *attr in
    attributesArr) {if(ABS > ABS(attr.center.x – centerX)) {minDelta
    = attr.center.x –
    centerX;}}//原本偏移量的x+距离宗旨点近期的cell到大旨点的离开
    将其安装为偏移量,该cell就能到基本点proposedContentOffset.x +=
    minDelta;return proposedContentOffset;}

效果图:![效果图-3.gif](http://upload-images.jianshu.io/upload_images/1914621-bc10257d76409d4d.gif?imageMogr2/auto-orient/strip)###总结:基本上到这里,UICollectionVew的使用就结束了,如何能够将UICollectionVew使用的更好,关键就在于怎样更好的运用UICollectionViewLayout和UICollectionViewFlowLayout,这才是UICollectionVew的精髓所在。有兴趣的同学,可以移步到GitHub中下载[Demo](https://github.com/muzesunshine/UICollectionViewDemo),里面我为大家做了非常详细的注释。

亚州城ca88手机版 6

三、采纳UICollectionView,使用自定义布局设置cell呈现(采纳)

1、自定义流水布局中,内定滚动方向、暗中同意列数、行间距、列间距、以及钦点cell的大小itemSize
2、可以提供四个数组column马克斯Ys(记录当前每一列的最大Y值),如若3列,大家就提供贰个3个因素的数组,记录全数布局属性

column马克斯Ys完成懒加载,
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.遍历column马克斯Ys数组,要求搜索最短一列的 列号 与 最大Y值

  1. 规定当前Cell的 寄存的x.y地点 以及widht与height
    —> width:依照显示屏宽度与列数以及每列的宽窄求出.
    height:服务器重返的
    —> x:需求依照如今最短一列的列号与Cell的升幅与间距能够求出来;y :
    能够依赖当下最短一列的最大Y值 + 行间距能够求出
    4.重新安装修改当前Cell的布局属性attrs 的 frame就可以。—>
    从前已经获得近年来Cell的
    暗许布局属性,以及上一步已经得到到眼下Cell需求贮存的x.y地点后,
    5.将修改Cell布局属性之后的,当前列当前Cell的Y值最大,全体大家要将该值记录到数组column马克斯Ys中,以便下一次比较
    5、注意:大家要求设置collectionView的contentSize,它才会滚动,那么大家怎么着设置呢?

——>
在概念布局类中,系统提供了三个collectionViewContentSize的get对象方法(决定collectionView的contentSize)
—>
contentSize的可观为:计算出最长那一列的最大Y值,也即是column马克斯Ys的最大值

  • 行间距

过于复杂.png

实现

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

亚州城ca88手机版 7

最优方案.png

二、瀑布流设计思路深入分析

1、自定义流水布局中,内定滚动方向、默许列数、行间距、列间距、以及钦赐cell的大小itemSize

2、能够提供几个数组column马克斯Ys(记录当前每一列的最大Y值),借使3列,大家就提供二个3个要素的数组,记录全体布局属性

column马克斯Ys达成懒加载,

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.遍历column马克斯Ys数组,供给寻觅最短一列的
列号 与 最大Y值3. 规定当前Cell的 贮存的x.y地方 以及widht与height     
—> width:依照显示屏宽度与列数以及每列的小幅度求出. 
height:服务器重回的      —>
x:需求依据近期最短一列的列号与Cell的宽窄与间距能够求出来;y:能够根据当下最短一列的最大Y值

  • 行间距能够求出4.双重设置修改当前Cell的布局属性attrs 的
    frame就能够。—> 从前早就得到当下Cell的
    暗许布局属性,以及上一步已经取获得近日Cell必要寄存的x.y地点后,5.将修改Cell布局属性之后的,当前列当前Cell的Y值最大,全数大家要将该值记录到数组column马克斯Ys中,以便下一次相比

5、注意:我们须要安装collectionView的contentSize,它才会滚动,那么我们如何设置呢?

——>
在概念布局类中,系统提供了多个collectionViewContentSize的get对象方法(决定collectionView的contentSize) 
                —>
contentSize的万丈为:总括出最长那一列的最大Y值,也正是column马克斯Ys的最大值

  • 行间距

三、瀑布流的主导完成

亚州城ca88手机版 8

亚州城ca88手机版,效果图.png

1.成立一个调整器JPCollectionViewController,承接UICollectionViewController,使用大家自定义的水流布局JPWaterflowLayout(完毕见其后)

#import”JPCollectionViewController.h”#import”JP沃特erflowLayout.h”//
自定义流水布局@interfaceJPCollectionViewController()@end@implementationJPCollectionViewControllerstaticNSString*constreuseIdentifier
=@”cellID”;- (void)viewDidLoad {    [superviewDidLoad];//
切换布局self.collectionView.collectionViewLayout = [[JPWaterflowLayout
alloc] init];}#pragma mark-
(NSInteger)collectionView:(UICollectionView*)collectionView
numberOfItemsInSection:(NSInteger)section {return30;}-
(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(NSIndexPath*)indexPath
{UICollectionViewCell*cell = [collectionView
dequeueReusableCellWithReuseIdentifier:reuseIdentifier
forIndexPath:indexPath];    cell.backgroundColor =
[UIColororangeColor];returncell;}@end

2.自定义流水布局JPWaterflowLayout,承继UICollectionViewLayout

#import”JPWaterflowLayout.h”#define JPCollectionW
self.collectionView.frame.size.width/** 每一行之间的间隔
*/staticconstCGFloatJPDefaultRowMargin =10;/** 每一列之间的距离
*/staticconstCGFloatJPDefaultColumnMargin =10;/** 每一列之间的间距
top, left, bottom, right */staticconstUIEdgeInsetsJPDefaultInsets =
{10,10,10,10};/** 默许的列数 */staticconstintJPDefaultColumsCount
=3;@interfaceJPWaterflowLayout()/** 每一列的最大Y值
*/@property(nonatomic,strong)NSMutableArray*columnMaxYs;/**
贮存全数cell的布局属性
*/@property(nonatomic,strong)NSMutableArray*attrsArray;@end@implementationJPWaterflowLayout#pragma
mark – 懒加载- (NSMutableArray*)columnMaxYs{if(!_columnMaxYs) {       
_columnMaxYs = [[NSMutableArrayalloc] init];   
}return_columnMaxYs;}- (NSMutableArray*)attrsArray{if(!_attrsArray)
{        _attrsArray = [[NSMutableArrayalloc] init];   
}return_attrsArray;}#pragma mark – 完结内部的艺术/**

* 决定了collectionView的contentSize

*/- (CGSize)collectionViewContentSize{//
搜索最长那一列的最大Y值CGFloatdestMaxY = [self.columnMaxYs[0]
doubleValue];for(NSUIntegeri =1; i

* 说明全体因素(举例cell、补充控件、装饰控件)的布局属性

*/-
(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{returnself.attrsArray;}/**

* 表明cell的布局属性

*/-

(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewLayoutAttributes*attrs

[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];/**
总计index帕特h地点cell的布局属性 */// 水平方向上的总间距CGFloatxMargin =
JPDefaultInsets.left + JPDefaultInsets.right + (JPDefaultColumsCount -1)
* JPDefaultColumnMargin;// cell的宽窄CGFloatw = (JPCollectionW –
xMargin) / JPDefaultColumsCount;// cell的冲天,测量试验数据,随机数CGFloath
=50+ arc4random_uniform(150);// 找出最短那一列的 列号 和
最大Y值CGFloatdest马克斯Y = [self.columnMaxYs[0]
doubleValue];NSUIntegerdestColumn =0;for(NSUIntegeri =1; i columnMaxY)
{            destMaxY = columnMaxY;            destColumn = i;        } 
  }// cell的x值CGFloatx = JPDefaultInsets.left + destColumn * (w +
JPDefaultColumnMargin);// cell的y值CGFloaty = dest马克斯Y +
JPDefaultRowMargin;// cell的frameattrs.frame =CGRectMake(x, y, w, h);//
更新数组中的最大Y值self.column马克斯Ys[destColumn] =
@(CGRectGetMaxY(attrs.frame));returnattrs;}@end

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注