iOS 瀑布流基本实现
2016-07-05 16:40
435 查看
一、瀑布流设计方案
![](http://upload-images.jianshu.io/upload_images/831339-89d62a6135ae9675.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
不可取.png
![](http://upload-images.jianshu.io/upload_images/831339-30b77090d57c24ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
过于复杂.png
![](http://upload-images.jianshu.io/upload_images/831339-1047406b74db6d0c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最优方案.png
二、瀑布流设计思路分析
1、自定义流水布局中,指定滚动方向、默认列数、行间距、列间距、以及指定cell的大小itemSize2、可以提供一个数组columnMaxYs(记录当前每一列的最大Y值),假如3列,我们就提供一个3个元素的数组,记录所有布局属性
columnMaxYs实现懒加载,
2.并在prepareLayout方法中 :
1.先将columnMaxYs清空。
2.再进行初始化每个元素为0.
3.获取所有的Cell的布局属性,而每一个Cell的布局属性通过调用layoutAttributesForItemAtIndexPath:方式获取,而调用该方法,就会执行第4步
(至于为什么在prepareLayout方法中初始化,而不是在init方法初始化: 是因为,该方法每次刷新都会调用,而init方法中只会在创建布局对象的时候只执行一次, 例如:如果进行下拉刷新最新数据的时候,需求重新初始化数据,而如果我们使用的是init方法的话,并不会调用也就并不会清除之前的然后初始化, 而使用该方法prepareLayout可以办到,因为它会再次调用,而init不会调用)
3.获取所有Cell的布局属性,
注意:对与所有Cell的布局属性,在第一次加载的时候需要计算一次,而当刷新collectionView的时候,当然也需要重新计算:所以我们提供一个布局属性的数组,来保存所有Cell的布局刷新,避免不必要的计算。 ——> 放在哪? 计算最好呢? ———> 首选放在prepareLayout方法中,因为它会调用一次加载,而且当刷新的时候也会调用,满足需求,我们先将之前的全部移除,然后重新返回最新的布局属性数组; 但是,最好不要放在layoutAttributesForElementsInRect:方法(返回所有元素的布局属性数组中),因为改方法在滚动collectionView的时候,会频繁的调用,比较销毁性能。
3、在layoutAttributesForElementsInRect:方法(返回所有元素的布局属性数组 )
返回之前保存的所有Cell的布局属性数组
4、我们可以在layoutAttributesForItemAtIndexPath: 方法: 来调整 Cell的布局属性 , 指定Cell的 frame
1.在该方法中拿到当前cell的默认布局属性attrs,进行下面的调整,就可以实现瀑布流了 2.遍历columnMaxYs数组,需要找出最短一列的 列号 与 最大Y值 3. 确定当前Cell的 存放的x.y位置 以及widht与height —> width:根据屏幕宽度与列数以及每列的宽度求出. height:服务器返回的 —> x:需要根据当前最短一列的列号与Cell的宽度与间距可以求出来;y : 可以根据当前最短一列的最大Y值 + 行间距可以求出 4.重新设置修改当前Cell的布局属性attrs 的 frame即可。—> 之前已经拿到当前Cell的 默认布局属性,以及上一步已经获取到当前Cell需要存放的x.y位置后, 5.将修改Cell布局属性之后的,当前列当前Cell的Y值最大,所有我们要将该值记录到数组columnMaxYs中,以便下次对比
5、注意:我们需要设置collectionView的contentSize,它才会滚动,那么我们如何设置呢?
——> 在定义布局类中,系统提供了一个collectionViewContentSize的get对象方法(决定collectionView的contentSize) —> contentSize的高度为:计算出最长那一列的最大Y值,也就是columnMaxYs的最大值 + 行间距
三、瀑布流的基本实现
![](http://upload-images.jianshu.io/upload_images/831339-b4de5b253754cfb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
效果图.png
1.创建一个控制器JPCollectionViewController,继承UICollectionViewController,使用我们自定义的流水布局JPWaterflowLayout(实现见其后)
#import "JPCollectionViewController.h" #import "JPWaterflowLayout.h" // 自定义流水布局 @interface JPCollectionViewController () @end @implementation JPCollectionViewController static NSString * const reuseIdentifier = @"cellID"; - (void)viewDidLoad { [super viewDidLoad]; // 切换布局 self.collectionView.collectionViewLayout = [[JPWaterflowLayout alloc] init]; } #pragma mark <UICollectionViewDataSource> - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 30; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; cell.backgroundColor = [UIColor orangeColor]; return cell; } @end
2.自定义流水布局JPWaterflowLayout,继承UICollectionViewLayout
#import "JPWaterflowLayout.h" #define JPCollectionW self.collectionView.frame.size.width /** 每一行之间的间距 */ static const CGFloat JPDefaultRowMargin = 10; /** 每一列之间的间距 */ static const CGFloat JPDefaultColumnMargin = 10; /** 每一列之间的间距 top, left, bottom, right */ static const UIEdgeInsets JPDefaultInsets = {10, 10, 10, 10}; /** 默认的列数 */ static const int JPDefaultColumsCount = 3; @interface JPWaterflowLayout() /** 每一列的最大Y值 */ @property (nonatomic, strong) NSMutableArray *columnMaxYs; /** 存放所有cell的布局属性 */ @property (nonatomic, strong) NSMutableArray *attrsArray; @end @implementation JPWaterflowLayout #pragma mark - 懒加载 - (NSMutableArray *)columnMaxYs { if (!_columnMaxYs) { _columnMaxYs = [[NSMutableArray alloc] init]; } return _columnMaxYs; } - (NSMutableArray *)attrsArray { if (!_attrsArray) { _attrsArray = [[NSMutableArray alloc] init]; } return _attrsArray; } #pragma mark - 实现内部的方法 /** * 决定了collectionView的contentSize */ - (CGSize)collectionViewContentSize { // 找出最长那一列的最大Y值 CGFloat destMaxY = [self.columnMaxYs[0] doubleValue]; for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) { // 取出第i列的最大Y值 CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue]; // 找出数组中的最大值 if (destMaxY < columnMaxY) { destMaxY = columnMaxY; } } return CGSizeMake(0, destMaxY + JPDefaultInsets.bottom); } - (void)prepareLayout { [super prepareLayout]; // 重置每一列的最大Y值 [self.columnMaxYs removeAllObjects]; for (NSUInteger i = 0; i<JPDefaultColumsCount; i++) { [self.columnMaxYs addObject:@(JPDefaultInsets.top)]; } // 计算所有cell的布局属性 [self.attrsArray removeAllObjects]; NSUInteger count = [self.collectionView numberOfItemsInSection:0]; for (NSUInteger i = 0; i < count; ++i) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath]; [self.attrsArray addObject:attrs]; } } /** * 说明所有元素(比如cell、补充控件、装饰控件)的布局属性 */ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { return self.attrsArray; } /** * 说明cell的布局属性 */ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; /** 计算indexPath位置cell的布局属性 */ // 水平方向上的总间距 CGFloat xMargin = JPDefaultInsets.left + JPDefaultInsets.right + (JPDefaultColumsCount - 1) * JPDefaultColumnMargin; // cell的宽度 CGFloat w = (JPCollectionW - xMargin) / JPDefaultColumsCount; // cell的高度,测试数据,随机数 CGFloat h = 50 + arc4random_uniform(150); // 找出最短那一列的 列号 和 最大Y值 CGFloat destMaxY = [self.columnMaxYs[0] doubleValue]; NSUInteger destColumn = 0; for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) { // 取出第i列的最大Y值 CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue]; // 找出数组中的最小值 if (destMaxY > columnMaxY) { destMaxY = columnMaxY; destColumn = i; } } // cell的x值 CGFloat x = JPDefaultInsets.left + destColumn * (w + JPDefaultColumnMargin); // cell的y值 CGFloat y = destMaxY + JPDefaultRowMargin; // cell的frame attrs.frame = CGRectMake(x, y, w, h); // 更新数组中的最大Y值 self.columnMaxYs[destColumn] = @(CGRectGetMaxY(attrs.frame)); return attrs; } @end
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- 讲解iOS开发中基本的定位功能实现
- iOS中定位当前位置坐标及转换为火星坐标的方法
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用开发中AFNetworking库的常用HTTP操作方法小结
- iOS应用中UISearchDisplayController搜索效果的用法
- iOS App开发中的UISegmentedControl分段组件用法总结
- IOS开发环境windows化攻略
- iOS应用中UITableView左滑自定义选项及批量删除的实现
- iOS中UIAlertView警告框组件的使用教程
- 浅析iOS应用开发中线程间的通信与线程安全问题
- iOS中的UIKeyboard键盘视图使用方法小结
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法