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

<1>第一种

效果图如下所示:

这种实现方法的思路:

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

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellW = ;
CGFloat cellH = + (arc4random() % );
[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 * + (num2 + ) * margin;
CGFloat cellY = ;
for (int i = ; i < num1; i++) {
NSInteger position = num2 + i * ;
cellY += [self.heightArrayM[position] floatValue] + margin;
}
CGFloat cellW = ;
CGFloat cellH = cellHeight;
cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
// cell.backgroundColor = [UIColor redColor];
cell.backgroundColor = [UIColor colorWithRed:(arc4random() % ) / 250.0 green:(arc4random() % ) / 250.0 blue:(arc4random() % ) / 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 ;
} - (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 * + (num2 + ) * margin;
CGFloat cellY = ;
for (int i = ; i < num1; i++) {
NSInteger position = num2 + i * ;
cellY += [self.heightArrayM[position] floatValue] + margin;
}
CGFloat cellW = ;
CGFloat cellH = cellHeight;
cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
// cell.backgroundColor = [UIColor redColor];
cell.backgroundColor = [UIColor colorWithRed:(arc4random() % ) / 250.0 green:(arc4random() % ) / 250.0 blue:(arc4random() % ) / 250.0 alpha:1.0]; // NSLog(@"%@", NSStringFromCGRect(cell.frame));
return cell;
} - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellW = ;
CGFloat cellH = + (arc4random() % );
[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: ) // 1.获取列数
let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? // 2.计算Item的宽度
let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - ))) / CGFloat(cols) // 3.计算所有的item的属性
for i in startIndex..<itemCount {
// 1.设置每一个Item位置相关的属性
let indexPath = IndexPath(item: i, section: ) // 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 + minimumLineSpacing
colHeights[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: , 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 UIKit

extension UIColor {
class func randomColor() -> UIColor {
return UIColor(colorLiteralRed: Float(arc4random_uniform()) / 255.0, green: Float(arc4random_uniform()) / 255.0, blue: Float(arc4random_uniform()) / 255.0, alpha: 1.0)
}
} private let kWaterCellID = "kWaterCellID" class ViewController: UIViewController { var count : Int = override func viewDidLoad() {
super.viewDidLoad() // 1.设置布局
let layout = WaterfallLayout()
layout.minimumLineSpacing =
layout.minimumInteritemSpacing =
layout.sectionInset = UIEdgeInsets(top: , left: , bottom: , right: )
layout.dataSource = self // 2.创建UICollectionView
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.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 - {
count += collectionView.reloadData()
} return cell
}
} extension ViewController : WaterfallLayoutDataSource {
func waterfallLayout(_ layout: WaterfallLayout, indexPath: IndexPath) -> CGFloat {
return CGFloat(arc4random_uniform() + )
} func numberOfColsInWaterfallLayout(_ layout: WaterfallLayout) -> Int {
return
}
}

封装自定义布局中的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 =
fileprivate lazy var colHeights : [CGFloat] = {
let cols = self.dataSource?.numberOfColsInWaterfallLayout?(self) ??
var colHeights = Array(repeating: self.sectionInset.top, count: cols)
return colHeights
}()
fileprivate var maxH : CGFloat =
fileprivate var startIndex =
} extension WaterfallLayout {
// prepare准备所有Cell的布局样式
override func prepare() {
super.prepare() // 0.获取item的个数
let itemCount = collectionView!.numberOfItems(inSection: ) // 1.获取列数
let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? // 2.计算Item的宽度
let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - ))) / CGFloat(cols) // 3.计算所有的item的属性
for i in startIndex..<itemCount {
// 1.设置每一个Item位置相关的属性
let indexPath = IndexPath(item: i, section: ) // 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 + minimumLineSpacing
colHeights[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: , height: maxH + sectionInset.bottom - minimumLineSpacing)
}
}

iOS瀑布流实现(Swift)的更多相关文章

  1. IOS 瀑布流UICollectionView实现

    IOS 瀑布流UICollectionView实现 在实现瀑布流之前先来看看瀑布流的雏形(此方法的雏形 UICollectionView) 对于UICollectionView我们有几点注意事项 它和 ...

  2. iOS 瀑布流之栅格布局

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

  3. iOS 瀑布流封装

    代码地址如下:http://www.demodashi.com/demo/12284.html 一.效果预览 功能描述:WSLWaterFlowLayout 是在继承于UICollectionView ...

  4. IOS 瀑布流

    本篇博客应该算的上CollectionView的高级应用了,从iOS开发之窥探UICollectionViewController(一)到今天的(五),可谓是由浅入深的窥探了一下UICollectio ...

  5. iOS 瀑布流的Demo

    /** * 瀑布流Demo的主要代码,若想看完整的代码请到下面链接去下载 * * 链接: https://pan.baidu.com/s/1slByAHB 密码: r3q6 */ #import &l ...

  6. iOS 瀑布流的基本原理

    /** * 源代码链接 * 链接: https://pan.baidu.com/s/1nvLamEX 密码: kya5 */ #import <UIKit/UIKit.h> @interf ...

  7. ios 瀑布流的那些事情

    转载: 屎壳郎情调-成长日记 首先要知道:瀑布流的核心就是要获取到图片的长宽 网上的很多例子都是加载本地图片的 对于新手而言 改成加载网络图片的确是有点压力的  因为本地的图片 我们是很容易就能获取到 ...

  8. ios瀑布流

    http://blog.csdn.net/shenjx1225/article/details/9037631

  9. iOS动画效果集合、 通过摄像头获取心率、仿淘宝滑动样式、瀑布流、分类切换布局等源码

    iOS精选源码 动画知识运用及常见动画效果收集 较为美观的多级展开列表 MUImageCache -简单轻量的图片缓存方案 iOS 瀑布流之栅格布局 一用就上瘾的JXCategoryView iOS ...

随机推荐

  1. oracle EXP导出一张表时使用query参数指定where条件

    oracle exp 导出一个表的部分内容,使用query参数可加上SQL的where条件进行过滤 注意:如果需要使用到日期字符串格式等单引号,需要使用双引号将where条件括起来,而且双引号要用\做 ...

  2. T-SQL Recipes之Database Backups

    The Problem 在DBA和T-SQL码奴日常工作中,比如常规检查,服务管理,数据库管理, 是其中最具挑战性的一个领域. 在相似任务中,比如索引碎片管理,统计管理,数据库备份是异常重要的,对任何 ...

  3. [Leetcode] Bulls and Cows

    You are playing the following Bulls and Cows game with your friend: You write a 4-digit secret numbe ...

  4. WP8解析JSON格式(使用Newtonsoft.Json包)

    DOTA2 WebAPI请求返回的格式有两种,一种是XML,一种是JSON,默认是返回JSON格式. 这里举一个简单的解析JSON格式的例子(更多JSON操作): { "response&q ...

  5. mysql中生产表格多列统计问题

    for Example: select date_format(date,'%Y-%m-%d') as day, count(case when xinghao='a' then 1 end) as  ...

  6. php安装配置那些事(本文纯属个人记事与技术无关)

    上周由于项目需要,又拿起了三年没动过的php,从安装环境到配置,大体已经忘干净,于是咨询同学问度娘,终于在我的win7系统下安装了xampp的集成环境+NetBeans IDE 8.0,于是导入项目文 ...

  7. Win32 OpenProcess打开进程失败,返回5无权限操作

    Win32 OpenProcess打开进程失败,返回5无权限操作,相信你会碰到这样的事,在IDE中可以,单独却不可以了,其实这时就需要提权了,否则是无法打开的,OpenProcess提权至Debug即 ...

  8. javascript模块化编程(三):require.js用法

    本文来自阮一峰 这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战. 我采用的是一个非常流行的库require.js. 一.为什么要用require ...

  9. pip安装MySQL-python报错

    pip install MySQL-pythonerror: command 'gcc' failed with exit status 1 yum install -y python-develpi ...

  10. 查看sbt版本

    进入 sbt 命令行模式, 键入sbtVersion 得到[info]0.13.12