iOS 13 UICollectionView新的布局方式

前言

众所周知,UICollectionView是一个非常灵活的控件,可以实现各种酷炫的样式,究其原因,是因为苹果巧妙地将UICollectionView的布局和渲染分隔开了,UICollectionView负责渲染,而UICollectionViewLayout负责布局。但是UICollectionViewLayout是一个抽象类,我们不能直接使用,需要定义他的子类来进行布局。

iOS 13之前

流式布局

在iOS 13之前,苹果为我们实现了一个具体的布局类UICollectionViewFlowLayout,它是UICollectionViewLayout的子类,通过类名就能看出,这个布局类实现的是流式布局,即按照“行”进行布局。举个例子,假如你的UICollectionView是垂直滚动的,那么这条“行”就是水平方向的,UICollectionView的item沿着水平方向进行填充,直到水平方向放不下则换到下一行继续沿着水平方向填充,结果如下图所示,当然,如果你的滚动方向是水平方向,那么这个“行”就是沿着垂直方向。如果你之前用过UICollectionView的话,那么对Flow布局肯定不陌生。

flow layout
自定义布局

UICollectionViewFlowLayout对于一些简单的布局还是很有用的,但是遇到复杂的布局就显得力不从心了,那么这个时候我们就需要自定义布局类进行操作,自定义布局类需要重写4个方法:

// 布局前的准备工作
- (void)prepareLayout

/**
 设置CollectionView的滚动区域
 layoutAttributesForElementsInRect方法调用之后都会调用该方法
 */
- (CGSize)collectionViewContentSize

/**
 返回指定区域所有cell的布局属性
 这个方法会多次执行,直到所有cell都显示出来就不执行了
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

// 返回IndexPath位置的item的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

本篇文章主要讲解iOS 13新的布局方式,而且代码中都有注释,这里就不具体讲解了。
虽然UICollectionViewLayout能实现各种灵活的效果,但是他也有自己的局限性——滚动方向只有一个,即不论你怎么自定义布局类,一个UICollectionView只能有一个滚动方向。
那如果我要实现UICollectionView不同section有不同的滚动方向要怎么做呢?iOS 13之前,你只能在UICollectionView中嵌套UICollectionView来实现,但是iOS 13之后就不需要了,因为iOS 13苹果为我们带来了一个全新的布局类——UICollectionViewCompositionalLayout

iOS 13

基本概念

UICollectionViewCompositionalLayout是苹果在iOS 13推出的全新的布局类,该类不再以子类化的方式定义布局,而是以组合的形式,因此该布局方式有3个特点:

  1. 组合
  2. 灵活
  3. 快速

UICollectionViewCompositionalLayout布局类主要包含4个部分:itemgroupsectionlayout,它们之间的关系如下图所示:

item、group、section和layout的关系

  1. item:这个不用过多解释,指的就是我们UICollectionView的每个cell和supplement view。
  2. group:这个是UICollectionViewCompositionalLayout新加的概念,group类似于流式布局,也是基于“行”进行的,可以是水平方向,也可以是垂直方向,group还可以嵌套group,这样就可以实现局部样式的多样化。
  3. section:等同于UICollectionView的section。
  4. layout:整个UICollectionView的布局。
核心类
  1. NSCollectionLayoutSize
  2. NSCollectionLayoutItem
  3. NSCollectionLayoutGroup
  4. NSCollectionLayoutSection
  5. UICollectionViewCompositionalLayout

第2~5分别对应上面4个部分,后面的代码会有介绍如何使用,这里重点介绍一下NSCollectionLayoutSize,这是苹果新推出的一个类,用来计算布局尺寸,它提供了一个类方法+ (instancetype)sizeWithWidthDimension:(NSCollectionLayoutDimension*)width heightDimension:(NSCollectionLayoutDimension*)height;,通过传入width和height,就能计算出相应的尺寸,需要注意的是,这里的width和height不是我们之前定义frame时的宽高,而是一个NSCollectionLayoutDimension的实例,NSCollectionLayoutDimension也是苹果新推出的一个类,用来定义尺寸大小,它提供了3种定义尺寸的方式——fractionalabsoluteestimated,对应的方法如下:

// dimension is computed as a fraction of the width of the containing group
+ (instancetype)fractionalWidthDimension:(CGFloat)fractionalWidth;

// dimension is computed as a fraction of the height of the containing group
+ (instancetype)fractionalHeightDimension:(CGFloat)fractionalHeight;

// dimension with an absolute point value
+ (instancetype)absoluteDimension:(CGFloat)absoluteDimension;

// dimension is estimated with a point value. Actual size will be determined when the content is rendered.
+ (instancetype)estimatedDimension:(CGFloat)estimatedDimension;

fractional是一个浮点数,表示视图的宽高占父视图宽高的百分比,值可以大于1。下图定义了一个高度是父视图高度30%的视图。

fractional

absolute是一个绝对的数值,表示一个具体的尺寸,跟frame的定义一样,单位是point。下图定义了一个高度是200个point的视图。

absolute

estimated是一个估算值,表示实际渲染的视图宽高可能变化,在self-sizing中使用。下图定义了一个估算值是200,实际高度是257的视图。

estimated

使用

理论知识讲的差不多了,下面就来实践一下吧。下面我们实现一个类似UITableView的简单列表,效果图如下:

UICollectionViewCompositionalLayout第一个Demo

该列表的布局代码如下:

- (UICollectionViewLayout *)generateLayout {
    // 1.item
    NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
    NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];
    // 2.group
    NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension absoluteDimension:44]];
    NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitem:item count:1];
    // 3.section
    NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
    // 4.layout
    return [[UICollectionViewCompositionalLayout alloc] initWithSection:section];
}

按照之前讲的理论知识,定义一个布局分4步:

  1. 定义一个item,宽高占父视图(group)的100%,即宽高等于父视图。
  2. 定义一个group,宽度占父视图的100%,高度等于44个point,它包含一个item
  3. 定义一个section,将group包含进来。
  4. section定义一个layout。

通过上面4步就完成了简单列表的布局。

总结

本篇文章介绍了iOS 13之前如何定义布局,以及iOS 13新推出的布局方式,用iOS 13新的布局方式实现了一个类似UITableView的简单列表,该列表虽然简单,但是包含了新布局方式的实现过程,复杂视图也只是上面步骤的变种,如果想查看其它样式的Demo,请点击链接查看,如果有写的不对的地方请指正,欢迎大家交流讨论,谢谢大家!

你可能感兴趣的:(iOS 13 UICollectionView新的布局方式)