iOS:UICollectionView纯自定义的布局:瀑布流布局

创建瀑布流有三种方式:

第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可。这种方式太奇葩,不太推荐使用...

第二种方式:在一个ScrollView里面从左到右依次放入三个UIView,当ScrollView滚动时,如果之前的三个view消失就将它们存入自定义的缓冲池,即数组中,下拉时再从数组中取出这三个view放到之前三个view位置的下面。但是,切记,每次要依次计算补全最短的,防止拉开的间距越来越大...,这种方式稍微麻烦一些…

第三种方式:其实就是上面第二种的简单版,就是使用UICollectionView,因为它有自己的重用机制,我们只需要做的就是计算下一次cell的位置即可....

集合视图的自动布局:UICollectionViewLayout是抽象根类,必须用它的子类才能创建实例,下面是重写的方法,计算item的布局属性

//每一次重新布局前,都会准备布局(苹果官方推荐使用该方法进行一些初始化)

-(void)prepareLayout

//重写layoutAttributesForItemAtIndexPath,返回每一个item的布局属性(流式布局内部已经帮助完成)

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

//是否要重新刷新布局(只要显示的item边界发生改变就重新布局)

//只要每一次重新布局内部就会调用下面的layoutAttributesForElementsInRect:获取所有cell(item)的属性

-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

//返回需要重新布局的所有item属性

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect

??:下面的瀑布流布局可以直接拿走使用

下面使用第三种方式创建的瀑布流代码如下:

WaterFlowLayout.h

#import <UIKit/UIKit.h>

/*
为了体现封装性的特点,我们可以把一些数据设置为公共的,既可以提高扩展性和通用性,
也便于外界按照自己的需求做必要的调整。
*/

@protocol WaterFlowLayoutDelegate; //设置代理传递数据,降低了与其他类的耦合性,通用性更强

@class WaterFlowLayout;
@interface WaterFlowLayout : UICollectionViewLayout
@property (assign,nonatomic)CGFloat columnMargin;//每一列item之间的间距
@property (assign,nonatomic)CGFloat rowMargin;   //每一行item之间的间距
@property (assign,nonatomic)UIEdgeInsets sectionInset;//设置于collectionView边缘的间距
@property (assign,nonatomic)NSInteger columnCount;//设置每一行排列的个数

@property (weak,nonatomic)id<WaterFlowLayoutDelegate> delegate; //设置代理
@end

@protocol WaterFlowLayoutDelegate
-(CGFloat)waterFlowLayout:(WaterFlowLayout *) WaterFlowLayout heightForWidth:(CGFloat)width andIndexPath:(NSIndexPath *)indexPath;
@end

WaterFloeLayout.m

#import "WaterFlowLayout.h"

//每一列item之间的间距
//static const CGFloat columnMargin = 10;
//每一行item之间的间距
//static const CGFloat rowMargin = 10;

@interface WaterFlowLayout()
/** 这个字典用来存储每一列item的高度 */
@property (strong,nonatomic)NSMutableDictionary *maxYDic;
/** 存放每一个item的布局属性 */
@property (strong,nonatomic)NSMutableArray *attrsArray;
@end

@implementation WaterFlowLayout

/** 懒加载 */
-(NSMutableDictionary *)maxYDic
{
    if (!_maxYDic)
    {
        _maxYDic = [NSMutableDictionary dictionary];
    }
    return _maxYDic;
}

/** 懒加载 */
-(NSMutableArray *)attrsArray
{
    if (!_attrsArray)
    {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

//初始化
-(instancetype)init
{
    if (self = [super init]){
        self.columnMargin = 10;
        self.rowMargin = 10;
        self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
        self.columnCount = 3;
    }
    return self;
}

//每一次布局前的准备工作
-(void)prepareLayout
{
    [super prepareLayout];

    //清空最大的y值
    for (int i =0; i < self.columnCount; i++)
    {
        NSString *column = [NSString stringWithFormat:@"%d",i];
        self.maxYDic[column] = @(self.sectionInset.top);
    }

    //计算所有item的属性
    [self.attrsArray removeAllObjects];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i=0; i<count; i++)
    {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];

        [self.attrsArray addObject:attrs];
    }
}

//设置collectionView滚动区域
-(CGSize)collectionViewContentSize
{
    //假设最长的那一列为第0列
    __block NSString *maxColumn = @"0";

    //遍历字典,找出最长的那一列
    [self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {

        if ([maxY floatValue] > [self.maxYDic[maxColumn] floatValue])
        {
            maxColumn = column;
        }
    }];
    return CGSizeMake(0, [self.maxYDic[maxColumn]floatValue]+self.sectionInset.bottom);
}

//允许每一次重新布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

//布局每一个属性
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    //假设最短的那一列为第0列
    __block NSString *minColumn = @"0";

    //遍历字典,找出最短的那一列
    [self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {

        if ([maxY floatValue] < [self.maxYDic[minColumn] floatValue])
        {
            minColumn = column;
        }
    }];

    //计算每一个item的宽度和高度
    CGFloat width = (self.collectionView.frame.size.width - self.columnMargin*(self.columnCount - 1) - self.sectionInset.left - self.sectionInset.right) / self.columnCount;

    CGFloat height = [self.delegate waterFlowLayout:self heightForWidth:width andIndexPath:indexPath] ;

    //计算每一个item的位置
    CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn floatValue];
    CGFloat y = [self.maxYDic[minColumn] floatValue] + self.rowMargin;

    //更新这一列的y值
    self.maxYDic[minColumn] = @(y + height);

    //创建布局属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    //设置item的frame
    attrs.frame = CGRectMake(x, y, width, height);

    return attrs;
}

//布局所有item的属性,包括header、footer
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}
@end

瀑布流演示结果如下:

时间: 2024-10-06 03:27:25

iOS:UICollectionView纯自定义的布局:瀑布流布局的相关文章

iOS:UICollectionView纯自定义的布局:堆叠式布局、圆式布局 (一般用来制作相册)

集合视图的自动布局:UICollectionViewLayout是抽象根类,必须用它的子类才能创建实例,下面是重写的方法,计算item的布局属性 //每一次重新布局前,都会准备布局(苹果官方推荐使用该方法进行一些初始化) -(void)prepareLayout //重写layoutAttributesForItemAtIndexPath,返回每一个item的布局属性(流式布局内部已经帮助完成) -(UICollectionViewLayoutAttributes *)layoutAttribu

iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局

iOS流布局UICollectionView系列四--自定义FlowLayout进行瀑布流布局 一.引言 前几篇博客从UICollectionView的基础应用到设置UICollectionViewFlowLayout更加灵活的进行布局,但都限制在系统为我们准备好的布局框架中,还是有一些局限性,例如,如果我要进行瀑布流似的不定高布局,前面的方法就很难满足我们的需求了,如下: 这种布局无疑在app的应用中更加广泛,商品的展示,书架书目的展示,都会倾向于采用这样的布局方式,当然,通过自定义FlowL

iOS UICollectionView 纯代码,无xib

// 1)  必须使用下面的方法进行Cell类的注册: //    - (void)registerClass:forCellWithReuseIdentifier: //    - (void)registerClass:forSupplementaryViewOfKind:withReuseIdentifier: //    - (void)registerNib:forCellWithReuseIdentifier: //    - (void)registerNib:forSupplem

JS瀑布流布局模式(1)

在实际的项目中,偶尔会用到一种布局——瀑布流布局.瀑布流布局的特点是,在多列布局时,可以保证内容区块在水平方向上不产生大的空隙,类似瀑布的效果.简单的说,在垂直列表里,内容区块是一个挨着一个的.当内容较多且不固定时,就依赖于html结构的顺序,非常受限制.这里给了一个简单的例子,只要传入列表的数量和宽度,就可以动态的将数据放到对应的列里. 原理 1.定义两个容器,一个是存放内容,一个是要展示的列表. 2.将每列的offsetHeight存入一个数组里,比较得出最小的那一列,然后把数据放到最小的列

iOS流布局UICollectionView系列六——将布局从平面应用到空间

iOS流布局UICollectionView系列六--将布局从平面应用到空间 一.引言 前面,我们将布局由线性的瀑布流布局扩展到了圆环布局,这使我们使用UICollectionView的布局思路大大迈进了一步,这次,我们玩的更加炫一些,想办法将布局应用的空间,你是否还记得,在管理布局的item的具体属性的类UICollectionViewLayoutAttributrs类中,有transform3D这个属性,通过这个属性的设置,我们真的可以在空间的坐标系中进行布局设计.iOS系统的控件中,也并非

iOS流布局UICollectionView系列五——圆环布局的实现

iOS流布局UICollectionView系列五--圆环布局的实现 一.引言 前边的几篇博客,我们了解了UICollectionView的基本用法以及一些扩展,在不定高的瀑布流布局中,我们发现,可以通过设置具体的布局属性类UICollectionViewLayoutAttributes来设置设置每个item的具体位置,我们可以再扩展一下,如果位置我们可以自由控制,那个布局我们也可以更加灵活,就比如创建一个如下的circleLayout: 这种布局方式在apple的官方文档中也有介绍,是UICo

iOS 美丽说瀑布流界面纯AutoLayout光速布局

最近在Github上看到三个库,分别是 GSKStretchyHeaderView,CHTCollectionViewWaterfallLayout ,JZNavigationExtension, 其中第一个是给CollectionView添加可动画的头部,添加方式和内容非常丰富,第二个是 瀑布流布局,第三个是UINavigationController的导航栏扩展.这三个东西厉害了啊,结 合起来咱们写个Demo,纯AutoLayout,效率非常高 (小弟搜集的好用的第三方库传送门:点击打开链接

ANDROID自定义视图——仿瀑布布局(附源码)

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():如何绘制这个View. 第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了. 第一步的测量,可以参考:(ANDROID自定义视图--onMeasure,MeasureSpec源码 流程 思路详解) 第二步的布局,可以参考:(AN

iOS开发 纯代码创建UICollectionView

转:http://jingyan.baidu.com/article/eb9f7b6d8a81a5869364e8a6.html iOS开发 纯代码创建UICollectionView 习惯了使用xib和StoryBoard创建UICollectionView项目工程的伙伴,需要转换使用纯代码来实现,想避免碰更多的壁,就需要认真了解创建UICollectionView过程了.创建UICollectionView比创建UITableView更加复杂,初始化方式也是相对奇特.以下是使用纯代码创建UI