自定义UICollectionLayout布局 —— UIKit之学习UICollectionView记录一《瀑布流》
一、思路
思路一:比较每一行所有列的cell的高度,从上到下(也就是从第一行开始),从最短的开始计算,(记录下b的高度和索引,从开始计算,依次类推)

思路二:设置上、下、左、右间距和行间距、列间距及列数。

思路三:实现的重要的方法。
二、代码先行。
1.自定义layout类。
//入口
#import <UIKit/UIKit.h> @protocol STRWaterLayoutDelegate; @interface STRWaterLayout : UICollectionViewLayout @property (nonatomic, weak)id<STRWaterLayoutDelegate>delegate; @end
@protocol STRWaterLayoutDelegate <NSObject> @required
- (CGFloat)waterLayout:(STRWaterLayout *)waterLayout layoutHeightAtindexPath:(NSIndexPath *)indexpath layoutItemWidth:(CGFloat)width;
@optional
- (UIEdgeInsets)edgeInsetsWithWaterLayout:(STRWaterLayout *)waterLayout;
- (CGFloat)columsNumberWithWaterLayout:(STRWaterLayout *)waterLayout;
- (CGFloat)rowsMarginWithWaterLayout:(STRWaterLayout *)waterLayout;
- (CGFloat)columsMarginWithWaterLaout:(STRWaterLayout *)waterLayout;
@end
//出口
#import "STRWaterLayout.h"
static const UIEdgeInsets layoutEdgeInsets = {, , , }; //上、下、左、右的间距
static const CGFloat columsMar = ; //列间距
static const CGFloat rowMar = ; //行间距
static const NSInteger columsNums = ; //列数 @interface STRWaterLayout() /**每个cell的高度等信息*/
@property(nonatomic, strong)NSMutableArray *columMinGapArray;
/**所有布局信息的数组*/
@property(nonatomic, strong)NSMutableArray *layoutAttributesArray;
/**布局的最终的高度*/
@property(nonatomic, assign)CGFloat contentHeight; - (UIEdgeInsets)edgeInsets; //上、下、左、右的间距
- (CGFloat)columsNumber; //列数
- (CGFloat)rowMargin; //行间距
- (CGFloat)columsMargin; //列间距
@end
@implementation STRWaterLayout #pragma mark --- delegate methods - (UIEdgeInsets)edgeInsets{ if (self.delegate && [self.delegate respondsToSelector:@selector(edgeInsetsWithWaterLayout:)]){ return [self.delegate edgeInsetsWithWaterLayout:self];
}else{
return layoutEdgeInsets;
}
}
- (CGFloat)columsNumber{ if(self.delegate && [self.delegate respondsToSelector:@selector(columsNumberWithWaterLayout:)]){
return [self.delegate columsNumberWithWaterLayout:self];
}else{
return columsNums;
}
}
- (CGFloat)columsMargin{
if(self.delegate && [self.delegate respondsToSelector:@selector(columsMarginWithWaterLaout:)]){
return [self.delegate columsMarginWithWaterLaout:self];
}else{
return columsMar;
}
}
- (CGFloat)rowMargin{
if(self.delegate && [self.delegate respondsToSelector:@selector(rowsMarginWithWaterLayout:)]){
return [self.delegate rowsMarginWithWaterLayout:self];
}else{
return rowMar;
}
}
#pragma mark --- private methods /**在重新布局时会依次调用这四个方法*/ /**每次重新布局时都会调用它*/
- (void)prepareLayout{
[super prepareLayout];
self.contentHeight = ;
[self.columMinGapArray removeAllObjects];
[self.layoutAttributesArray removeAllObjects];
for (NSInteger i = ; i< [self columsNumber]; i++) { //遍历所有的列数
[self.columMinGapArray addObject: @([self edgeInsets].top)];
}
//当前只有一组,所以这么处理
NSInteger numbers = [self.collectionView numberOfItemsInSection:];
//处理所有的布局数据
for (NSInteger y = ; y <numbers;y++) {
//calls layouts methods,调用布局的方法
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:y inSection:]];
//再把算出来的布局加到布局数组中。
[self.layoutAttributesArray addObject:layoutAttributes];
}
}
/**返回横向滚动或纵向滚动的contentSize*/
- (CGSize)collectionViewContentSize{
return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight+[self edgeInsets].bottom);
}
/**返回所有UICollectionViewLayoutAttributes的属性的数组*/
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.layoutAttributesArray;
}
/**返回每个indexPath的布局UICollectionViewLayoutAttributes*/
// 1.首先通过indexPath取出每个collectionViewLayout的布局(uicollectionlayoutAttributes).
// 2.从存储每个cell的高度的数组中取出第一个cell的高度,然后遍历,存储最小高度的cell,并记下它的索引,然后在这个索引下的cell增加下一个cell,然后再遍历看看哪个cell的高度最低,再把它的高度和索引记下来,增加下下个cell,依次类推。
// 3.因为布局的宽度和x,y可以自己设定(也就是能控制),不能控制的是布局的高度,需要从外部传进来,因为需要等比例的,所以需要传当前indexPath的布局的宽度,然后外面传的时候用cell的实际宽度*实际高度/当前布局的宽度。
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; CGFloat minContentHeight = [self.columMinGapArray[] doubleValue];
NSInteger minIndex = ;
for (NSInteger i = ; i < self.columMinGapArray.count; i++) {
if (minContentHeight > [self.columMinGapArray[i] doubleValue]) {
minContentHeight = [self.columMinGapArray[i] doubleValue];
minIndex = i;
}
}
CGFloat w = (self.collectionView.frame.size.width - [self edgeInsets].left - [self edgeInsets].right - ([self columsNumber]-) * [self columsMargin])/[self columsNumber]; CGFloat y = (minContentHeight == [self edgeInsets].top ? minContentHeight : minContentHeight + [self rowMargin]); CGFloat x = [self edgeInsets].left + minIndex *(w+[self columsMargin]); CGFloat h = [self.delegate waterLayout:self layoutHeightAtindexPath:indexPath layoutItemWidth:w]; layoutAttributes.frame = CGRectMake(x, y, w, h);
self.columMinGapArray[minIndex] = @(CGRectGetMaxY(layoutAttributes.frame)); CGFloat contentHeight = [self.columMinGapArray[minIndex] doubleValue];
if (self.contentHeight < contentHeight) {
self.contentHeight = contentHeight;
}
return layoutAttributes;
} #pragma mark --- getters and setters
- (NSMutableArray *)columMinGapArray{
if (_columMinGapArray == nil) { _columMinGapArray = [NSMutableArray array];
}
return _columMinGapArray;
}
- (NSMutableArray *)layoutAttributesArray{
if (_layoutAttributesArray == nil) {
_layoutAttributesArray = [NSMutableArray array];
}
return _layoutAttributesArray;
}
@end
2.设置collectionView.
1).注意事项1,需要设置布局,上面创建的layout布局。
2).注意事项2,需要设置代理和数据源,然后把必须实现的方法实现一下。
3).注意事项3,注册cell有两种形式,一个是xib,一个是自定义的cell类,自定义或xib的cell类一定是设置collectionViewcell方法中的cell对象。
- (UICollectionView *)collectionView{
if (!_collectionView) {
STRWaterLayout *layout = [[STRWaterLayout alloc] init];
layout.delegate = self;
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , self.view.bounds.size.width,self.view.bounds.size.height) collectionViewLayout:layout];
collectionView.delegate = self;
collectionView.dataSource = self;
collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(headRefresh)];
collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(foorRefresh)];
[collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:customCell];
[self.view addSubview:collectionView];
self.collectionView = collectionView;
}
return _collectionView;
}
3.实现collectionView的数据源方法或代理方法。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.dataArray.count; //存储的是布局模型数据,有宽度、高度(最基本的)及其它对象数据。
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- ( UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:customCell forIndexPath:indexPath];
cell.layoutModel = self.dataArray[indexPath.row];
return cell;
}
4.获取数据。(我这里是用的plist,这个可以根据项目来设置,这里是举例。)

- (NSMutableArray *)dataArray{
if (!_dataArray) {
NSArray *data = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"" ofType:@"plist"]];
NSMutableArray *dataA = [NSMutableArray array];
for (NSDictionary *dic in data) {
STRWaterLayoutModel *layoutModel = [STRWaterLayoutModel getCollectionModel:dic];
[dataA addObject:layoutModel];
}
_dataArray = dataA;
}
return _dataArray;
}
5.实现布局layout的代理,传入当前cell的高度和间距。
- (CGFloat)waterLayout:(STRWaterLayout *)waterLayout layoutHeightAtindexPath:(NSIndexPath *)indexpath layoutItemWidth:(CGFloat)width{
STRWaterLayoutModel *waterLayoutModel = [self.dataArray objectAtIndex:indexpath.row];
// return width * [waterLayoutModel.h doubleValue] / [waterLayoutModel.w doubleValue];
return [waterLayoutModel.h doubleValue];
}
- (CGFloat)columsNumberWithWaterLayout:(STRWaterLayout *)waterLayout{
return ;
}
- (CGFloat)columsMarginWithWaterLaout:(STRWaterLayout *)waterLayout{
return ;
}
- (CGFloat)rowsMarginWithWaterLayout:(STRWaterLayout *)waterLayout{
return ;
}
- (UIEdgeInsets)edgeInsetsWithWaterLayout:(STRWaterLayout *)waterLayout{
return UIEdgeInsetsMake(,, , );
}
三、总结。
1.通过上面瀑布流的核心思想。
2.创建collectionView的一些注意事项。
3.给某个类传数据时,可以用代理,这才是正儿八经遵守MVC的思想,后期改动的话,好扩展,如果用属性的话,后期会很麻烦。
4.代码地址:待后期上传。
自定义UICollectionLayout布局 —— UIKit之学习UICollectionView记录一《瀑布流》的更多相关文章
- UICollectionLayout布局 —— UIKit之学习UICollectionView记录二《流水布局》
重点知识 一. 加载collectionView注意事项 1.创建collectionView,有两种方式 :一种是xib和一种是纯代码:设置代理和数据源,注册cell,配置流水布局的属性,如上.下. ...
- Swift:用UICollectionView整一个瀑布流
本文的例子和Swift版本是基于Xcode7.2的.以后也许不知道什么时候会更新. 我们要干点啥 用新浪微博的Open API做后端来实现我们要提到的功能.把新浪微博的内容,图片和文字展示在colle ...
- iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流
上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...
- UICollectionView Layout自定义 Layout布局
from: http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...
- iOS:UICollectionView纯自定义的布局:瀑布流布局
创建瀑布流有三种方式: 第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可. ...
- 详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形…)
前言: 本篇文章不是分享collectionView的详细使用教程, 而是属于比较’高级’的collectionView使用技巧, 阅读之前, 我想你已经很熟悉collectionView的基本使用, ...
- 详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形...)
前言: 本篇文章不是分享collectionView的详细使用教程, 而是属于比较'高级'的collectionView使用技巧, 阅读之前, 我想你已经很熟悉collectionView的基本使用, ...
- 自定义 Layout布局 UICollectionViewLayout
from: http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...
- Objectiv-c - UICollectionViewLayout自定义布局-瀑布流
最近刚写的一个简单的瀑布流. 整体思路可能不是很完善. 不过也算是实现效果了. 高手勿喷 思路: 自定义UICollectionViewLayout实际上就是需要返回每个item的fram就可以了. ...
随机推荐
- 转载:《TypeScript 中文入门教程》 10、混入
版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 除了传统的面向对象继承方式,还流行一种通过可重用组件创建类的方式,就是联合另一个 ...
- [翻译]用 Puppet 搭建易管理的服务器基础架构(4)
我通过伯乐在线翻译了一个Puppet简明教程,一共分为四部分,这是第四部分. 原文地址:http://blog.jobbole.com/89214/ 本文由 伯乐在线 - Wing 翻译,黄利民 校稿 ...
- nodejs 命令行、自定义
一.必备插件 1. babel:es6语法支持,需要babel-perset-es2015(转换成es5执行).babel.babel-core(程序执行) 2. commander:自定义命令插件, ...
- GIS管网项目-flex/java
开发语言是flex.java,开发平台是myeclise.eclise,后台数据库是oracel或sqlserver,开发接口是arcgis api for flex,提供以下的功能: 1.应急指挥: ...
- 解决 安装cocoapods失败,提示 requires Ruby version >=2.2.2
步骤如下: rvm install ruby-2.2 但是,但是竟然报错了,具体我忘记额,但是是权限和brew的问题,那么我又转向修复brew: // 清理原来brew rm -rf /usr/loc ...
- UITextFeild的用法
一. 修改占位字符串的 颜色: =======方法一 ====================================== #import "ViewController.h&quo ...
- iPhone 6 被盗记录二【写在315前夕:苹果售后福州直信创邺在没有三包的情况下帮小偷翻新、助力小偷换机销赃!无视王法。让人震惊,痛心,憎恨!消费者很受伤很无奈】
投诉公司: 北京直信创邺数码科技有限公司 标题: 写在315前夕:苹果售后在没有三包的情况下帮小偷翻新.助力小偷换机销赃!无视王法.让人震惊,痛心,憎恨!消费者很受伤很无奈 期望: 还我手机,或者赔 ...
- 基于Windows 10平台的PM2.5检测器制作
本篇文章详细讲解了如何利用SDS011激光式PM2.5传感器.HC-06蓝牙模块和Windows 10设备完成一个简单的PM2.5检测器及其应用程序的开发.该检测器使用蓝牙完成数据输出,方便设备连接, ...
- Linux时间同步介绍
在Linux系统中,为了避免主机时间因为在长时间运行下所导致的时间偏差,进行时间同步(synchronize)的工作是非常必要的.Linux系统下,一般使用ntp服务来同步不同机器的时间.NTP 是网 ...
- asp.net signalR 专题—— 第一篇 你需要好好掌握的实时通讯利器
一:背景 我们知道传统的http采用的是“拉模型”,也就是每次请求,每次断开这种短请求模式,这种场景下,client是老大,server就像一个小乌龟任人摆布, 很显然,只有一方主动,这事情就没那么完 ...