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
复制代码

 

瀑布流演示结果如下:

 

github: https://github.com/xiayuanquan/XYQCollectionLayout.git

 

程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!
本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4985560.html如需转载请自行联系原作者

相关文章
|
6月前
|
iOS开发 UED
实现一个自定义的iOS动画效果
【4月更文挑战第9天】本文将详细介绍如何在iOS平台上实现一个自定义的动画效果。我们将通过使用Core Animation框架来实现这个动画效果,并展示如何在不同的场景中使用它。文章的目标是帮助读者理解如何使用Core Animation框架来创建自定义动画,并提供一个简单的示例代码。
52 1
|
10天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
25 1
|
19天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
43 5
|
26天前
|
Swift iOS开发 UED
实现一个自定义的iOS动画效果
本文介绍如何使用Swift和UIKit在iOS应用中实现一个自定义按钮动画,当按钮被点击时,其颜色从蓝色渐变为绿色,形状从圆形变为椭圆形,释放后恢复原状。通过UIView动画方法实现这一效果,代码示例展示了动画的平滑过渡和状态切换,有助于提升应用的视觉体验和用户交互。
49 1
|
2月前
|
安全 iOS开发
iOS页面布局:UIScrollView的布局问题
iOS页面布局:UIScrollView的布局问题
57 8
|
2月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
61 11
|
3月前
|
Swift iOS开发 UED
【绝妙创意】颠覆你的视觉体验!揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【8月更文挑战第13天】本文通过一个具体案例,介绍如何使用Swift与UIKit在iOS应用中创建独特的按钮动画效果。当按钮被按下时,其形状从圆形变化为椭圆形,颜色则从蓝色渐变为绿色;释放后,动画反向恢复原状。利用UIView动画方法及弹簧动画效果,实现了平滑自然的过渡。通过调整参数,开发者可以进一步优化动画体验,增强应用的互动性和视觉吸引力。
50 7
|
iOS开发
iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结
iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结
461 0
|
API iOS开发
iOS 自定义转场动画 UIViewControllerTransitioning
iOS 自定义转场动画 UIViewControllerTransitioning
91 0
|
iOS开发
iOS布局中的抗被拉伸、抗压缩优先级
iOS布局中的抗被拉伸、抗压缩优先级
659 0