from:   http://www.tuicool.com/articles/vuyIriN

当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以考虑自定义layout。

所以,了解并学习一下自定义Layout是很有必要。

其实可以分三个步骤:

  1. 覆写prepareLayout方法,并在里面事先就计算好必要的布局信息并存储起来。
  2. 基于prepareLayout方法中的布局信息,使用collectionViewContentSize方法返回UICollectionView的内容尺寸。
  3. 使用layoutAttributesForElementsInRect:方法返回指定区域cell、Supplementary View和Decoration View的布局属性。

了解了自定义布局的三个主要步骤,我们来通过自定义布局的方式用UICollectionView实现grideView。当然,grideView使用 UICollectionViewFlowLayout 就可以轻易实现,这里我们只是学习了解一下自定义布局的过程,所以拿grideView这个经常用的来作为例子。

我们创建一个新的工程 BGCustomLayoutCollectionViewDemo 。然后创建一个UICollectionViewLayout的子类对象 BGGrideLayout ,它就是我们自定义layout对象。

在BGGrideLayout里面,我们首先覆写prepareLayout方法。

prepareLayout是专门用来准备布局的,在 prepareLayout 方法里面我们可以事先就计算后面要用到的布局信息并存储起来,防止后面方法多次计算,提高性能。例如,我们可以在此方法就计算好每个cell的属性、整个CollectionView的内容尺寸等等。此方法在布局之前会调用一次,之后只有在调用 invalidateLayout 、 shouldInvalidateLayoutForBoundsChange: 返回 YES 和 UICollectionView刷新 的时候才会调用。

而在BGGrideLayout的prepareLayout方法中,我们有两个目的:

一是获取对应indexPath的 UICollectionViewLayoutAttributes 对象,并存储到二维数组 layoutInfoArr 当中;

二是计算出内容尺寸并保存到全局变量 contentSize 当中。

代码如下:

- (void)prepareLayout{
[super prepareLayout];
NSMutableArray *layoutInfoArr = [NSMutableArray array];
NSInteger maxNumberOfItems = 0;
//获取布局信息
NSInteger numberOfSections = [self.collectionView numberOfSections];
for (NSInteger section = 0; section < numberOfSections; section++){
NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];
NSMutableArray *subArr = [NSMutableArray arrayWithCapacity:numberOfItems];
for (NSInteger item = 0; item < numberOfItems; item++){
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[subArr addObject:attributes];
}
if(maxNumberOfItems < numberOfItems){
maxNumberOfItems = numberOfItems;
}
//添加到二维数组
[layoutInfoArr addObject:[subArr copy]];
}
//存储布局信息
self.layoutInfoArr = [layoutInfoArr copy];
//保存内容尺寸
self.contentSize = CGSizeMake(maxNumberOfItems*(self.itemSize.width+self.interitemSpacing)+self.interitemSpacing, numberOfSections*(self.itemSize.height+self.lineSpacing)+self.lineSpacing);
}

在上面的代码中,我们看到了 UICollectionViewLayoutAttributes 这个类,这个类其实专门用来存储视图的内容,例如frame、size、apha、hiden等等,layout最后会拿着这些frame设置给对应的视图。 而上面代码中,获取 UICollectionViewLayoutAttributes 是通过 layoutAttributesForItemAtIndexPath: 方法

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//每一组cell为一行
attributes.frame = CGRectMake((self.itemSize.width+self.interitemSpacing)*indexPath.row+self.interitemSpacing, (self.itemSize.height+self.lineSpacing)*indexPath.section+self.lineSpacing, self.itemSize.width, self.itemSize.height);
return attributes;
}

在这个方法中, itemSize 是cell的大小, interitemSpacing 是cell与cell之间的间距, lineSpacing 是行距。

随后,覆写collectionViewContentSize

collectionViewContentSize返回内容尺寸给UICollectionView。注意这个方法返回的尺寸是给UICollectionView这个继承于 UIScrollView 的视图作为 contentSize ,不是UICollectionView的视图尺寸。正是因为这一点,我们自定义layout如果想让它只能横向滑动,只需要将这个 size.height 设置成 collectionView.height 就行了。 这个方法会多次调用,所以最好是在prepareLayout里就计算好。 在BGGrideLayout类中,我们只需要返回前面计算好的内容尺寸就行了。

- (CGSize)collectionViewContentSize{
return self.contentSize;
}

最后,覆写layoutAttributesForElementsInRect:方法

此方法需要返回一组UICollectionViewLayoutAttributes类型对象。它代表着在这个指定的区域中,我们需要显示 cell 、 Supplementary View 和 Decoration View中哪些视图,而这些视图的属性则保存UICollectionViewLayoutAttributes中。 此方法会多次调用,为了更好的性能,在这个方法当中,我们使用的UICollectionViewLayoutAttributes最好是在prepareLayout已经布局好的信息。

在BGGrideLayout中,我们遍历二维数组,找出了与指定区域有交接的UICollectionViewLayoutAttributes对象放到一个数组中,然后返回。

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray *layoutAttributesArr = [NSMutableArray array];
[self.layoutInfoArr enumerateObjectsUsingBlock:^(NSArray *array, NSUInteger i, BOOL * _Nonnull stop) {
[array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if(CGRectIntersectsRect(obj.frame, rect)) {
[layoutAttributesArr addObject:obj];
}
}];
}];
return layoutAttributesArr;
}

到这里,我们的BGGrideLayout已经写好了,使用部分的代码,请直接查看 BGCustomLayoutCollectionViewDemo 中ViewController里面的代码就行了。

UICollectionView Layout自定义 Layout布局的更多相关文章

  1. UICollectionView之自定义Layout

    #import <UIKit/UIKit.h> @interface WQViewController : UIViewController - (id)initWithFrame:(CG ...

  2. iOS:UICollectionView纯自定义的布局:瀑布流布局

    创建瀑布流有三种方式:   第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可. ...

  3. iOS:UICollectionView纯自定义的布局:堆叠式布局、圆式布局 (一般用来制作相册)

    集合视图的自动布局:UICollectionViewLayout是抽象根类,必须用它的子类才能创建实例,下面是重写的方法,计算item的布局属性 //每一次重新布局前,都会准备布局(苹果官方推荐使用该 ...

  4. 自定义 Layout布局 UICollectionViewLayout

    from:   http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...

  5. 自定义UICollectionLayout布局 —— UIKit之学习UICollectionView记录一《瀑布流》

    一.思路 思路一:比较每一行所有列的cell的高度,从上到下(也就是从第一行开始),从最短的开始计算,(记录下b的高度和索引,从开始计算,依次类推) 思路二:设置上.下.左.右间距和行间距.列间距及列 ...

  6. 详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形…)

    前言: 本篇文章不是分享collectionView的详细使用教程, 而是属于比较’高级’的collectionView使用技巧, 阅读之前, 我想你已经很熟悉collectionView的基本使用, ...

  7. 安卓自定义控件(四)实现自定义Layout

    本来我是不准备写这篇文章的,我实在想不出有什么样奇怪的理由,会去继承ViewGroup然后自定义一个布局,大概是我的项目经验还不够吧,想了好久,想到了这样一个需求: 需求 如图:在项目中经常有一个这样 ...

  8. 详细分享UICollectionView的自定义布局(瀑布流, 线性, 圆形...)

    前言: 本篇文章不是分享collectionView的详细使用教程, 而是属于比较'高级'的collectionView使用技巧, 阅读之前, 我想你已经很熟悉collectionView的基本使用, ...

  9. Android Studio分类整理res/Layout中的布局文件(创建子目录)

    res/layout中的布局文件太杂,没有层次感,受不了的我治好想办法解决这个问题. 前几天看博客说可以使用插件分组,可惜我没找到.知道看到另一篇博客时,才知道这个方法不能用了. 不能用插件,那就手动 ...

随机推荐

  1. VxVM vxsnap ERROR V-5-1-0 Volume cannot be linked due to size/regionsize incompatibility

    在做vxsnap addmir时报错如下: #> vxsnap -g OLS_DATA_DG -b addmir OLS_DATA_ACC_P mirvol=OLS_DATA_ACC_SM1 V ...

  2. ORA-00600:内部错误代码,参数:[kpnxdcbk-2],[],[],[],[],[],[],[],[],[],[],[]

    由于最近工作中常出现ORA-00600:内部错误代码,参数:[kpnxdcbk-2],[],[],[],[],[],[],[],[],[],[],[]这种异常!所以在这里讲一下我的处理方法. 笔者所遇 ...

  3. OpenCV 官方工程报错(1) Couldn't load mixed_sample from loader

    openCV/OpenCV-android-sdk/samples/tutorial-2-mixedprocessing 工程 - ::): Trying to get library list - ...

  4. MD5 加密算法的使用

    最近在看视频时,看到 MD5 的加密算法,感觉其在某些重要信息中,还是很好的解决了一些安全问题的.于是,就在自己理解的情况下,实现了 MD5 算法. 具体的流程大致是: (1)将指定的数据首先通过 M ...

  5. socket多线程方式案例

    记下来,方便以后查看 User类 package com.xujingyang.ThreadSocket; import java.io.Serializable; public class User ...

  6. 磨刀——python及相关工具

    1.python语言包 1.1去https://www.python.org/,在download栏下载最新版python2或者python3 tips:1.点击下载会很慢,推荐:迅雷,百度云盘下载, ...

  7. Mysql date, time, timestamp日期时间相关

    date: 格式:YYYY-MM-DD,时间范围:[0000-00-00, 9999-12-31],存储空间:3bytes time: 格式:HH:MM:SS,时间范围:[00:00:00, 23:5 ...

  8. CoreData的增删改查

    首先使用CoreData创建Demo,勾上CoreData选项 然后创建Entity对象,点击Add Entity(+)按钮 生成Entity对象 重命名双击Entity选项,然后输入Person 设 ...

  9. python 简单的数据库操作之转账

    介绍:本文是关于数据库的简单操作,实现转账(只是修改数据库中用户的账户金额)的功能 模块介绍:首先是入口主函数 主函数中实现转账方法  以及异常的处理: if __name__ == "__ ...

  10. java的类型转换问题。int a = 123456;short b = (short)a;System.out.println(b);为什么结果是-7616?

    这个从二进制来解释: int 是 32 位,也就是最多能表示长度为 32 位的二进制整数.第一位是符号位,表示正负,0 表示正的,1 表示负的.当首位为 1(为负)时,把所有的位取反(0 变成 1,1 ...