美文网首页
iOS 13 UICollectionView新的布局方式

iOS 13 UICollectionView新的布局方式

作者: StephenCurry300 | 来源:发表于2019-11-12 16:34 被阅读0次

    前言

    众所周知,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<UICollectionViewLayoutAttributes *> *)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新的布局方式

          本文链接:https://www.haomeiwen.com/subject/uptqictx.html