这段时间突然想到一个很久之前用到的知识-瀑布流,本来想用一个简单的方法,发现自己走入了歧途,最终只能狠下心来重写UICollectionViewFlowLayout.下面我将用两种方法实现瀑布流,以及会介绍第一种实现的bug.

<1>第一种

效果图如下所示:

这种实现方法的思路:

1)首先调用随机函数,产生随机高度,并把它保存到数组中

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {CGFloat cellW = 100;CGFloat cellH = 100 + (arc4random() % 80);[self.heightArrayM addObject:@(cellH)];return CGSizeMake(cellW, cellH);}

 2)在设置cell的frame的地方,通过取余,取整确定cell的高度,并设定cell的frame

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];//当前处于多少行NSInteger num1 = indexPath.row / count;//当前处于多少列int num2 = indexPath.row % count;CGFloat cellX = num2 * 100 + (num2 + 1) * margin;CGFloat cellY = 0;for (int i = 0; i < num1; i++) {NSInteger position =  num2 + i * 3;cellY += [self.heightArrayM[position] floatValue] + margin;}CGFloat cellW = 100;CGFloat cellH = cellHeight;cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
//    cell.backgroundColor = [UIColor redColor];cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];//    NSLog(@"%@", NSStringFromCGRect(cell.frame)); return cell;
}

弊端 : 其实这种方法的弊端,相信从上面的动态图中可以看出来,当往上面滑的时候,由于cell的循环机制,下面的cell的会消失,但是由于高度不一致,同时撤销的是最后一行的cell,所以下面的cell在屏幕上就会消失.

下面附上第一种方法的源代码:

#import "ViewController.h"#define margin 10
#define count 3
#define cellHeight [self.heightArrayM[indexPath.row] floatValue]
static NSString * const ID = @"cell";
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *heightArrayM;@end@implementation ViewController- (NSMutableArray *)heightArrayM {if (_heightArrayM == nil) {_heightArrayM = [NSMutableArray array];}return _heightArrayM;
}- (void)viewDidLoad {[super viewDidLoad];[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:ID];self.collectionView.dataSource = self;self.collectionView.delegate = self;//设置collectionView
    [self setupCollectionView];
}//设置collectionView的布局
- (UICollectionViewFlowLayout *)setupCollectionLayout {UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];flowLayout.minimumInteritemSpacing = margin;flowLayout.minimumLineSpacing = margin;flowLayout.sectionInset = UIEdgeInsetsMake(margin, margin, margin, margin);return flowLayout;
}//设置collectionView
- (void)setupCollectionView {self.collectionView.collectionViewLayout =[self setupCollectionLayout];}#pragma mark - UICollectionViewDataSouce
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return 60;
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];//当前处于多少行NSInteger num1 = indexPath.row / count;//当前处于多少列int num2 = indexPath.row % count;CGFloat cellX = num2 * 100 + (num2 + 1) * margin;CGFloat cellY = 0;for (int i = 0; i < num1; i++) {NSInteger position =  num2 + i * 3;cellY += [self.heightArrayM[position] floatValue] + margin;}CGFloat cellW = 100;CGFloat cellH = cellHeight;cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
//    cell.backgroundColor = [UIColor redColor];cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];//    NSLog(@"%@", NSStringFromCGRect(cell.frame)); return cell;
}- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {CGFloat cellW = 100;CGFloat cellH = 100 + (arc4random() % 80);[self.heightArrayM addObject:@(cellH)];return CGSizeMake(cellW, cellH);}
@end

<2>下面介绍第二种(Swift实现)

效果图如下所示:

这种实现方法就是比较成熟的了,我把它封装成一个类.其实主要是实现三个函数

1)重写父类的prepare方法,准备所有cell的样式

extension WaterfallLayout {// prepare准备所有Cell的布局样式override func prepare() {super.prepare()// 0.获取item的个数let itemCount = collectionView!.numberOfItems(inSection: 0)// 1.获取列数let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2// 2.计算Item的宽度let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)// 3.计算所有的item的属性for i in startIndex..<itemCount {// 1.设置每一个Item位置相关的属性let indexPath = IndexPath(item: i, section: 0)// 2.根据位置创建Attributes属性let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)// 3.随机一个高度guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {fatalError("请设置数据源,并且实现对应的数据源方法")}// 4.取出最小列的位置var minH = colHeights.min()!let index = colHeights.index(of: minH)!minH = minH + height + minimumLineSpacingcolHeights[index] = minH// 5.设置item的属性attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)attrsArray.append(attrs)}// 4.记录最大值maxH = colHeights.max()!// 5.给startIndex重新复制startIndex = itemCount}
}

  2)返回设置cell样式的数组

 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {return attrsArray}

  3)返回当前的contentSize

override var collectionViewContentSize: CGSize {return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)}

总结:

在下面我封装的这个类中,只需要遵守我的数据代理源协议并且实现我的协议中的两个方法,传给我对应得高度(我这里是传的随机的),可选的方法,若是不实现,会有一个默认值,就可以实现该功能.协议如下:

@objc protocol WaterfallLayoutDataSource : class {func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int
}

完成代码如下所示:
ViewController.swift中的代码:

import UIKitextension UIColor {class func randomColor() -> UIColor {return UIColor(colorLiteralRed: Float(arc4random_uniform(256)) / 255.0, green: Float(arc4random_uniform(256)) / 255.0, blue: Float(arc4random_uniform(256)) / 255.0, alpha: 1.0)}
}private let kWaterCellID = "kWaterCellID"class ViewController: UIViewController {var count : Int = 20override func viewDidLoad() {super.viewDidLoad()// 1.设置布局let layout = WaterfallLayout()layout.minimumLineSpacing = 10layout.minimumInteritemSpacing = 10layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)layout.dataSource = self// 2.创建UICollectionViewlet collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)collectionView.dataSource = selfcollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: kWaterCellID)view.addSubview(collectionView)}}extension ViewController : UICollectionViewDataSource {func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return count}func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kWaterCellID, for: indexPath)cell.backgroundColor = UIColor.randomColor()if indexPath.item == count - 1 {count += 20collectionView.reloadData()}return cell}
}extension ViewController : WaterfallLayoutDataSource {func waterfallLayout(_ layout: WaterfallLayout, indexPath: IndexPath) -> CGFloat {return CGFloat(arc4random_uniform(80) + 100)}func numberOfColsInWaterfallLayout(_ layout: WaterfallLayout) -> Int {return 3}
}

封装自定义布局中的WaterfallLayout.swift代码如下:

import UIKit@objc protocol WaterfallLayoutDataSource : class {func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int
}class WaterfallLayout: UICollectionViewFlowLayout {// MARK: 对外提供属性weak var dataSource : WaterfallLayoutDataSource?// MARK: 私有属性fileprivate lazy var attrsArray : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()fileprivate var totalHeight : CGFloat = 0fileprivate lazy var colHeights : [CGFloat] = {let cols = self.dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2var colHeights = Array(repeating: self.sectionInset.top, count: cols)return colHeights}()fileprivate var maxH : CGFloat = 0fileprivate var startIndex = 0
}extension WaterfallLayout {// prepare准备所有Cell的布局样式override func prepare() {super.prepare()// 0.获取item的个数let itemCount = collectionView!.numberOfItems(inSection: 0)// 1.获取列数let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2// 2.计算Item的宽度let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)// 3.计算所有的item的属性for i in startIndex..<itemCount {// 1.设置每一个Item位置相关的属性let indexPath = IndexPath(item: i, section: 0)// 2.根据位置创建Attributes属性let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)// 3.随机一个高度guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {fatalError("请设置数据源,并且实现对应的数据源方法")}// 4.取出最小列的位置var minH = colHeights.min()!let index = colHeights.index(of: minH)!minH = minH + height + minimumLineSpacingcolHeights[index] = minH// 5.设置item的属性attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)attrsArray.append(attrs)}// 4.记录最大值maxH = colHeights.max()!// 5.给startIndex重新复制startIndex = itemCount}
}extension WaterfallLayout {override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {return attrsArray}override var collectionViewContentSize: CGSize {return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)}
}

转载于:https://www.cnblogs.com/muzichenyu/p/6108040.html

iOS瀑布流实现(Swift)相关推荐

  1. iOS 瀑布流之栅格布局

    代码地址如下: http://www.demodashi.com/demo/14760.html 一 .效果预览 二.确定需求 由下面的需求示意图可知模块的最小单位是正方形,边长是屏幕宽除去边距间隔后 ...

  2. iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流

    在上一篇博客中<iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流>,自定义瀑布流的列数,Cell的外边距,C ...

  3. iOS 两个tableview的 瀑布流

    iOS 两个tableview的 瀑布流 1. [代码]Objective-C      // //  DocViewController.m //  getrightbutton // //  Cr ...

  4. 【iOS开发每日小笔记(二)】gitHub上的开源“瀑布流”使用心得

    这篇文章是我的[iOS开发每日小笔记]系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧.它们可能会给用户体验.代码效率得到一些提升,或是之前自己 ...

  5. iOS基础UI瀑布流界面简单搭建

    ios UI基础瀑布流 顾名思义是将界面以瀑布流水般的展现出来,使用瀑布流,首先对数据进行懒加载,传入数据后,使用UIcollectionView控件在main.storyboard里进行简单的布局 ...

  6. Swift:瀑布流 网络加载图片 Swift 5.0

    上代码: // // WaterfallLayout.swift // WaterfallSwift // // Created by admin on 2019/10/11. // Copyrigh ...

  7. 图片瀑布流ios中部分显示空白

    @ios中的web页面图片布局闪动或者显示空白之类问题的解决方法 出现问题的代码(问题现象:ios手机中 商品图片 只有少部分加载成功其余出现大量空白 ) 现在购买再送 80% .pro_img im ...

  8. IOS开发 多section瀑布流+悬停Header OC

    写在前面的话 问题:在实习期间才开始学习IOS开发,项目中要求实现类似小红书首页效果的瀑布流,包括多种Section,每种Section有对应的头部HeaderView,以及上滑时实现Header悬停 ...

  9. iOS UICollectionView实现瀑布流(3)

    前面两篇Blog简单的介绍了UICollection的基本使用并实现了类似Android的Gallery效果,这篇文章使用UICollection来实现瀑布流效果,代码主要是在极客学院Carol老师的 ...

最新文章

  1. sql 忽略大小写_Flink使用Calcite解析Sql做维表关联(一)
  2. 有关定时任务的表达式--cron 详细解
  3. 【数据挖掘】基于密度的聚类方法 - OPTICS 方法 ( 核心距离 | 可达距离 | 族序 )
  4. 深度解析dba_segments和sys.seg$中的细节差异(下)
  5. java 及时通讯弹幕技术 视频播放 websocket SSM 集成代码生成器 源码
  6. Qt Creator寻找
  7. 新知丨口服益生菌是商业噱头?
  8. C++ 左值和右值 的学习
  9. mqtt协议详解_阿里云物联网平台使用心得(七)MQTT协议14个报文总览
  10. 格式 数组的基本使用 0912
  11. oracle Plsql 执行update或者delete时卡死问题解决办法
  12. Python 文件处理命令
  13. php 两位小数 00,php保存两位小数的几种要领引见_后端开发
  14. OpenCV-图像处理(19、Canny边缘检测)
  15. 73本免费的、语言无关的优秀的编程书籍
  16. 刀与剑-C++ COM组件调用
  17. 《Kotlin从零到精通Android开发》面世啦
  18. 通过PCF8591读取电压值(AD转换)
  19. nvidia xavier平台无PD控制器USB接口调试
  20. CVPR 2022 | 提高小数据集利用效率,复旦等提出分层级联ViT网络

热门文章

  1. 3.7 代码注释和编码规范
  2. .NET项目是否有必要升级到.NET 3.5 + VS 2008
  3. 中央暗示:07年别急买房
  4. MONGODB 权限认证
  5. 【配置DATAGUARD 时的监听静态注册不成功】 问题解决
  6. android 弹出对话框时显示键盘
  7. MyBatis入门HelloWorld,注解实现
  8. [九省联考2018]IIIDX 贪心 线段树
  9. PyCharm----快捷键
  10. ionic上拉加载-下拉刷新