写在前面的话

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

解决办法:在网上搜索了很多资料,乱七八糟的,很多讲的迷迷糊糊而且代码没有注释!!这对一个ios开发的新手来说是极其不友好的。不过我还是发现了一篇比较不错的文章,但是他只实现了一种Section的瀑布流,并不足够完成我的项目,然后我就继续搜索重复的做无用功,最后的结果还是。。。不行。但是我并没有放弃,开始自己搞,最后终于搞懂里面的原理,并最终实现了我需要的效果,下面我将提供给大家一种思路去解决以上问题。

正文

  1. 首先了解如何实现瀑布流

    通过查资料,实现瀑布流的方法有多种,最常见的就是用UICollectionView去实现,因此我就采取这种方法去实现它。

  2. 如何通过UICollectionView实现瀑布流

    认真了解过UICollectionView后,你肯定知道UICollectionView的初始化,必须要有一个Layout


collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];

collectionViewLayout: layout。而实现瀑布流的关键就在这个Layout!!!通过自定义的CollectionViewLayout就可以实现瀑布流其他各种操作,下面介绍自定义Layout

  1. 先贴代码,然后再去分析
//  FallsFlowLayout.h文件
#import <UIKit/UIKit.h>@interface FallsFlowLayout : UICollectionViewLayout@property (nonatomic, assign, readonly) CGFloat itemWidth;//单元格宽度
@property (nonatomic, assign) NSInteger numberOfColumns;//列数:默认为2
@property (nonatomic, assign) UIEdgeInsets insets;//内边距 : 每一列之间的间距 (top, left, bottom, right)默认为{10, 10, 10, 10};
@property (nonatomic, assign) CGFloat rowGap;//每一行之间的间距 : 默认为10
@property (nonatomic, assign) CGFloat columnGap;//每一列之间的间距 : 默认为10
@property (nonatomic, strong) NSMutableArray *itemHeights;//高度数组 : 存储所有item的高度
@property (nonatomic, assign) NSUInteger sectionCount;// section数目,默认为2@end
// FallsFlowLayout.m文件#import "FallsFlowLayout.h"
#import "UIUtils.h" //这个是我自定义的工具类,这里主要是取kScreenWidth(屏幕宽度)@interface FallsFlowLayout()@property (nonatomic, strong) NSMutableArray *itemAttributes; // 存放每个cell的布局属性
@property (nonatomic, strong) NSMutableArray *columnsHeightsOfSection; // 每个Section的每一列的高度,二维数组
@property (nonatomic, assign) NSInteger stickHeight;//悬停Header的高度@end@implementation FallsFlowLayout#pragma mark- 懒加载
- (NSMutableArray *)columnsHeightsOfSection {if (!_columnsHeightsOfSection) {_columnsHeightsOfSection = [NSMutableArray array];}return _columnsHeightsOfSection;
}- (NSMutableArray *)itemAttributes {if (!_itemAttributes) {_itemAttributes= [NSMutableArray array];}return _itemAttributes;
}#pragma mark- 初始化方法
- (instancetype)init {if(self= [super init]) {// 初始化默认值self.numberOfColumns = 2;//默认列数为2self.columnGap=10;//默认列间距为10self.rowGap=10;//默认行间距为10self.insets = UIEdgeInsetsMake(10, 10, 10, 10);//默认UICollectionView的内边距为10self.stickHeight=44;//悬停的Header高度self.sectionCount=2;//默认为2}return self;
}#pragma mark- get方法/// 获取单元格宽度
- (CGFloat)itemWidth {//( collectionView的宽度 - 列间距*列数 ) / 列数return (self.collectionView.frame.size.width - (self.numberOfColumns + 1)*self.columnGap) / self.numberOfColumns;
}/// 获取某个section的最短列的编号
/// @param section 分区
- (NSUInteger)minIndexOfSection:(NSUInteger)section {NSInteger minIndex =0;CGFloat minHeight =MAXFLOAT;for(NSInteger i =0; i <self.numberOfColumns; i ++) {// 取出某一列的高度与其他的比较,求得高度最短的列的 列号CGFloat currentHeight = [self.columnsHeightsOfSection[section][i]floatValue];if(currentHeight < minHeight) {minHeight = currentHeight;minIndex = i;}}return minIndex;
}/// 获取某个section的最短列的编号
/// @param section 分区
- (NSUInteger)maxIndexOfSection:(NSUInteger)section {NSInteger maxIndex =0;CGFloat maxHeight =0;for(NSInteger i =0; i <self.numberOfColumns; i ++) {CGFloat currentHeight = [self.columnsHeightsOfSection[section][i]floatValue];if(currentHeight > maxHeight) {maxHeight = currentHeight;maxIndex = i;}}return maxIndex;
}/// 获取某个section的最短高度
/// @param section 分区
- (CGFloat)minHeightOfSection:(NSUInteger)section {// 该section 最短列的列号NSUInteger minIndex = [self minIndexOfSection:section];return[self.columnsHeightsOfSection[section][minIndex]floatValue];
}/// 获取某个section的最大高度
/// @param section 分区
- (CGFloat)maxHeightOfSection:(NSUInteger)section {NSUInteger maxIndex = [self maxIndexOfSection:section];return[self.columnsHeightsOfSection[section][maxIndex]floatValue];
}#pragma mark- 系统内部方法
/// 重写父类布局
- (void)prepareLayout {[super prepareLayout];// 重置每一个Section的每一列的最大Y值为Header高度[self.columnsHeightsOfSection removeAllObjects];for(inti =0;i <self.sectionCount;i++){NSMutableArray*columnsHeights = [NSMutableArray array];for(intj =0;j <self.numberOfColumns;j++){// 每个Section的Header高度,我的Header高度为44[columnsHeightsaddObject:@(44)];}[self.columnsHeightsOfSection addObject:columnsHeights];}// 计算所有cell的布局属性,包括HeaderView的,先清空[self.itemAttributes removeAllObjects];// HeaderNSMutableArray<UICollectionViewLayoutAttributes *> *layoutHeader = [NSMutableArray arrayWithCapacity:self.sectionCount];// 跑这个Section循环是为了实现多Sectionfor(int section =0;section <self.sectionCount;section++){// 头部视图layoutHeader[section] = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathWithIndex:section]];// Header的y坐标为 前面Section的最大高度之和int y =0;for(int i =0;i < section;i++)y += [self maxHeightOfSection:i];// 我的HeaderView的高度为44layoutHeader[section].frame=CGRectMake(0,y,kScreenWidth,44);// 将Header的属性添加到数组[self.itemAttributes addObject:layoutHeader[section]];// 计算所有cell的布局属性,看好下面的sectionNSUInteger itemCount = [self.collectionView numberOfItemsInSection:section];for(NSUIntegeri =0; i < itemCount; ++i) {NSIndexPath*indexPath = [NSIndexPath indexPathForItem:i inSection:section];[self setItemFrame:indexPath];}}
}/// 设置每一个attrs的frame,并加入数组中
/// @param indexPath 第几个item
- (void)setItemFrame:(NSIndexPath*)indexPath {/*** 注:1.cell的宽度和高度算起来比较简单 : 宽度固定(itemWidth已经算好),高度由外部传进来*    2.cell的x : minIndex最短列作为当前列。*    3.cell的y : 把前面所有Section的最大高度求和 + 该Section的最短列高度*/UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];// cell的宽度和高度CGFloat w =self.itemWidth;CGFloat h = [self.itemHeights[indexPath.item]floatValue];// 最短列编号NSUInteger minIndex = [self minIndexOfSection:indexPath.section];CGFloat x =self.insets.left+ minIndex * (w +self.columnGap);// 前面几个section的最大高度之和 + 该section的最短列高度CGFloat topSectionHeight =0;CGFloat minHeight =0;for(inti =0;i < indexPath.section;i++)topSectionHeight += [self maxHeightOfSection:i];// 加上该section的最短列高度minHeight += [self minHeightOfSection:indexPath.section];CGFloaty =topSectionHeight + minHeight +self.rowGap;attrs.frame=CGRectMake(x, y, w, h);// 更新该Section的高度self.columnsHeightsOfSection[indexPath.section][minIndex] =@(h + minHeight +self.rowGap);[self.itemAttributes addObject:attrs];
}/// 返回collectionView的尺寸
- (CGSize)collectionViewContentSize {CGFloat maxHeight =0;for(inti =0;i <self.sectionCount; i++){maxHeight += [self maxHeightOfSection:i];}return CGSizeMake(self.collectionView.frame.size.width, maxHeight);
}/// 所有元素(比如cell、补充控件、装饰控件)的布局属性
/// @param rect 尺寸
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{[self sectionHeaderStickCounter];//Header停留return self.itemAttributes;
}#pragma mark- Header 停留
/// Header停留
- (void)sectionHeaderStickCounter
{for (UICollectionViewLayoutAttributes *layoutAttributes in self.itemAttributes) {if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {CGPoint origin = layoutAttributes.frame.origin;if(layoutAttributes.indexPath.section==0){// 只要往上一滑动,就将Header的y修改为collectionView.contentOffset.y,至于为什么是这个数,可以打个Log输出看看结果// self.collectionView.contentOffset.y 是相对于CollectionView的content的相对y移动if (self.collectionView.contentOffset.y >= 0)origin.y=self.collectionView.contentOffset.y;// 如果滑动到该下一个Section的Header上来了,则固定第一个的Header的y值if(self.collectionView.contentOffset.y>= [self maxHeightOfSection:0] -self.stickHeight)origin.y= [self maxHeightOfSection:0] -self.stickHeight;}else{// 这是默认两个Section的情况,如果有更多种情况,自己计算一下,加几个else if就好了if(self.collectionView.contentOffset.y>= [self maxHeightOfSection:0])origin.y=self.collectionView.contentOffset.y;}CGFloatwidth = layoutAttributes.frame.size.width;layoutAttributes.zIndex = 2048;//设置一个比cell的zIndex大的值,让他浮于其他Cell之上layoutAttributes.frame= (CGRect){.origin= origin,.size=CGSizeMake(width, layoutAttributes.frame.size.height)};}}
}/// 不设置这里看不到悬停
/// @param newBoundsnewBounds description
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{return YES;
}@end

4 .分析代码
还有一个部分,如何使用没有写,下次一起。
代码分析下次再分析,可以先复制代码试试,有问题可以加我VX:13864023718。

IOS开发 多section瀑布流+悬停Header OC相关推荐

  1. iOS开发:AVPlayer实现流音频边播边存

    1. AVPlayer简介AVPlayer存在于AVFoundation中,可以播放视频和音频,可以理解为一个随身听AVPlayer的关联类:AVAsset:一个抽象类,不能直接使用,代表一个要播放的 ...

  2. 【原创】iOS开发入门教程

    2019独角兽企业重金招聘Python工程师标准>>> 程序发轻狂,代码阑珊,苹果开发安卓狂!--写给狂热的编程爱好者们 ###写在前面的话 学习iOS应用程序开发已有一段时间,最近 ...

  3. 如何学习才能快速成为ios开发高手?

    ios开发初学者,该从哪学起? 1 假设你非常急想换工作,借钱上培训班.培训班对比自学,最大的优势在于有输送渠道,你学完后他们会帮你联系工作 2 假设你经济窘迫,还是一边工作一边自学,同时找公司里面的 ...

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

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

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

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

  6. ios开发瀑布流框架的封装

    一:瀑布流框架封装的实现思路:此瀑布流框架的封装仿照tableView的底层实现,1:每个cell的frame的设置都是找出每列的最大y值,比较每列的最大y值,将下一个cell放在最大y值最小的那一列 ...

  7. IOS开发之异步加载网络图片并缓存本地实现瀑布流(一)

    2019独角兽企业重金招聘Python工程师标准>>> 版权声明:本文为博主原创文章,未经博主允许不得转载. [objc] view plaincopy </pre>&l ...

  8. iOS开发之瀑布流照片墙实现

    想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳.现在网上流行一种叫做"瀑布流"的照片布局样式,这种行与 ...

  9. iOS 两个tableview的 瀑布流

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

最新文章

  1. QT5.3 + vs2012 + box2d环境配置
  2. vs目录(继承的值)配置
  3. git 版本操作命令大全
  4. linux生成地图,ROS中利用V-rep进行地图构建仿真
  5. Java修炼之路——基础篇——Java集合类详解2
  6. 斯坦福大学的机器学习课程,浓缩成6张速查表
  7. STL sort 函数实现详解 ZZ
  8. 苹果被拒:Guideline 5.0 - Legal
  9. 使用链接时间代码生成
  10. C语言全局变量重复定义检查-fno-common
  11. 2012 苹果第三代iPad3(NewPAD)发布会图文实录
  12. python数字时钟
  13. web前端开发前景如何?
  14. 地产大佬潘石屹宣布入坑Python,这是要来抢我们的饭碗了吗?
  15. ESP8266-Arduino编程实例-TTP223 电容式触摸传感器驱动
  16. Android仿keep运动轨迹动画,仿keep思路
  17. 万变不离其宗之ZYNQ启动介绍
  18. 宋红康老师JVM课程学习笔记
  19. matlab 邦加球,吴先良(软件学院)老师 - 安徽大学
  20. safari 扩展_为什么构建Safari应用扩展程序是2020年的噩梦

热门文章

  1. Windows cmd切换盘符,切换文件夹
  2. 根据excel字段信息动态生成建表语句sql
  3. 耶鲁计算机科学专业,耶鲁大学(Yale University)计算机科学Computer Science专业排名第29位(2021年THE世界大学商科排名)...
  4. 看电影经常遇到的简单口语
  5. 李峋 同款代码,用html来进行表白
  6. 适合零基础学习的IT编程技术
  7. ubuntu20.04系统下中文输入法的安装与使用
  8. 低通、高通数字滤波器——C语言单片机实现
  9. QGraphicsScene QGraphicsView QGraphicsItem
  10. Jenkins部署瘦身jar包