这段时间突然想到一个很久之前用到的知识-瀑布流,本来想用一个简单的方法,发现自己走入了歧途,最终只能狠下心来重写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 + 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: 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 UIKit extension 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 = 20 override func viewDidLoad() { super.viewDidLoad() // 1.设置布局 let layout = WaterfallLayout() layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 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 - 1 { count += 20 collectionView.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 = 0 fileprivate lazy var colHeights : [CGFloat] = { let cols = self.dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2 var colHeights = Array(repeating: self.sectionInset.top, count: cols) return colHeights }() fileprivate var maxH : CGFloat = 0 fileprivate 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 + 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: 0, height: maxH + sectionInset.bottom - minimumLineSpacing) } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍IOS简单实现瀑布流UICollectionView,包括了IOS简单实现瀑布流UICollectionView的使用技巧和注意事项,需要的朋友参考一下 UICollectionView 比tableView 灵活,功能也强大很多。系统实现了流式布局,但用处还有很多限制。 要想实现更灵活的布局,就咬重写UICollectionViewLayout。 先看下实现效果: 废话不多说,直接
本文向大家介绍jquery实现简单的瀑布流布局,包括了jquery实现简单的瀑布流布局的使用技巧和注意事项,需要的朋友参考一下 是开头都会说的原理 瀑布流布局有两种,一种是固定列,一种是非固定列。在此主要记述第一种的实现。 固定列的特征是:无论页面如何缩放,每行的总列数都一致。 一行4列的瀑布流从布局的角度来说,就是4个li标签。通过一定的事件(比如滚动条滚动多少px),然后读取之,再把数据动态地
本文向大家介绍IOS实现自定义布局瀑布流,包括了IOS实现自定义布局瀑布流的使用技巧和注意事项,需要的朋友参考一下 瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UICollectionView实现(通常采用的方式) 一、UICollectionView基础 1、
本文向大家介绍iOS实现水平方向瀑布流,包括了iOS实现水平方向瀑布流的使用技巧和注意事项,需要的朋友参考一下 效果 源码:https://github.com/YouXianMing/Animations 细节 继承UICollectionViewLayout 重载UICollectionViewLayout的四个方法 部分实现细节 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家
本文向大家介绍ios基于UICollectionView实现横向瀑布流,包括了ios基于UICollectionView实现横向瀑布流的使用技巧和注意事项,需要的朋友参考一下 在网上找了许久,一直没有发现有提供横向瀑布流效果的。在项目中用到了我就在垂直瀑布流的基础上,进行了修改,做出了横向瀑布流的效果。同时也对一些UICollectionView的属性进行简单的注释,方便以后查阅。 1、首先要写一
本文向大家介绍js瀑布流布局的实现,包括了js瀑布流布局的实现的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了js实现瀑布流布局的具体代码,供大家参考,具体内容如下 原理: 1、瀑布流布局,要求进行布局的元素等宽,然后计算元素的宽与浏览器的宽度之比,得到需要布置的列数。 2、创建一个数组,长度为列数,数组元素为每一列已布置元素的总高度。(一开始为0)。 3、将未布置的元素,依次布置到