初探UICollectionViewCompositionalLayout

因为UICollectionViewCompositionalLayout,再次爱上UICollectionView。

最近在做横向滚动的UICollectionView的时候,cell不是全屏的,又想实现按cell分页效果而不是按屏幕宽度分页,我注意到AppStore的游戏和App tab页面横向滚动的效果非常棒,在搜索过程中发现了UICollectionViewCompositionalLayout关键字,遂把玩之。(UICollectionViewCompositionalLayout在WWDC2019就推出的,然而我并没有看WWDC,也不是第一时间更新到最新系统去吃螃蟹者,写到这里其实内心有点惭愧)

至于怎么在iOS13以下实现按cell分页效果的,感兴趣的可以看上篇:Paging UICollectionView by smaller cells, not screen

UICollectionViewCompositionalLayout是苹果在iOS13推出的一个高度自适应和灵活的布局对象,它是UICollectionView的一种布局方式,它的设计是可组合的、灵活的和快速的。

我们先看下iOS13上AppStore上的页面效果,嵌套交叉滚动,算是正常的需求了,如果要我们做,我们肯定是用类似cell上嵌套UICollectionView的思路来实现,我们不仅要写纵向的cell样式、横向的cell样式,还要处理嵌套cell的点击事件,简直是太繁琐了。

AppStore

现在,有了UICollectionViewCompositionalLayout这把金刚钻,整个页面我们使用一个UICollectionView即可实现,在我们配置好UICollectionViewCompositionalLayout布局样式后,剩下的,放眼望去,满屏皆是这一个UICollectionView的cell,是不是很简单?(苹果公司终于考虑到了这一点,然而啥时候才能最低支持iOS13?)

下面来结合demo一起解开UICollectionViewCompositionalLayout的神秘面纱。

主要概念

UICollectionViewCompositionalLayout.png

由图可见,层级关系为 NSCollectionLayoutItem -> NSCollectionLayoutGroup -> NSCollectionLayoutSection

其中:

  • NSCollectionLayoutDimension 维度尺寸
    用来创建NSCollectionLayoutSize,有按占group的比例创建尺寸fractionalWidthDimension:fractionalHeightDimension:,按固定值创建尺寸absoluteDimension:、按预估值创建尺寸estimatedDimension:的方法。

  • NSCollectionLayoutSize 布局尺寸
    NSCollectionLayoutItem和NSCollectionLayoutGroup在初始化时都需要通过其指定尺寸

  • NSCollectionLayoutItem 布局单元
    可以配置尺寸、装饰视图

  • NSCollectionLayoutGroup 分组
    是NSCollectionLayoutItem的子类,支持横向、纵向、自定义分组。注意interItemSpacing属性只对NSCollectionLayoutGroup创建时指定了多个item的才有效。

  • NSCollectionLayoutSection 分区
    与group绑定,可以通过boundarySupplementaryItems制定组头组尾、通过visibleItemsInvalidationHandler监听滚动,通过orthogonalScrollingBehavior设置在交叉轴上如何滚动与分页。

  • UICollectionViewCompositionalLayout 组合布局
    与NSCollectionLayoutSection绑定,initWithSectionProvider方可以满足为UICollectionView的每个Section配置不同的NSCollectionLayoutSection。

NSCollectionLayoutItem的对齐方式

edgespacing

首先,这里创建了一个group,包含一大一小不同的item,我们想让小的item水平垂直居中,主要用到了item的edgeSpacing属性,这里使用NSCollectionLayoutSpacing创建了一个距离边界都保持相同的灵活间距。这里注意一点,这里创建group时指定了多个item,所以设置interItemSpacing属性才有效果,如果只指定了一个item,该属性不起效。

//大item 宽占group的一半,高占满group
NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.5] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];

//小item 宽占group的四分之一,高占group的一半
NSCollectionLayoutSize *itemSize1 = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.25] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:0.5]];
NSCollectionLayoutItem *item1 = [NSCollectionLayoutItem itemWithLayoutSize:itemSize1];
item1.contentInsets = NSDirectionalEdgeInsetsMake(20, 20, 20, 20);//内边距 会改变其size
//item1.edgeSpacing = [NSCollectionLayoutEdgeSpacing spacingForLeading:nil top:[NSCollectionLayoutSpacing flexibleSpacing:10] trailing:nil bottom:nil];//外边距,值可能会被优化 【相对于group底部对齐】
item1.edgeSpacing = [NSCollectionLayoutEdgeSpacing spacingForLeading:[NSCollectionLayoutSpacing flexibleSpacing:10] top:[NSCollectionLayoutSpacing flexibleSpacing:10] trailing:[NSCollectionLayoutSpacing flexibleSpacing:10] bottom:[NSCollectionLayoutSpacing flexibleSpacing:10]];//【相对于group居中对齐】

//一个group可以指定多个item,指定多个,对应的cell会按items的样式依次布局
NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.25]];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitems:@[item, item1]];
//interItemSpacing对指定了多个subitems才有效
group.interItemSpacing = [NSCollectionLayoutSpacing fixedSpacing:5];

//一个section只能指定一个group
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
section.contentInsets = NSDirectionalEdgeInsetsFromString(@"{5.0, 5.0, 5.0, 5.0}");
    
//layout只能指定一个section
UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc]initWithSection:section configuration:config];

组合group、组头组尾、补充视图

组合group&组头组尾&badge

由于NSCollectionLayoutGroup继承自NSCollectionLayoutItem,在创建NSCollectionLayoutGroup时需要指定NSCollectionLayoutItem,所以,我们就可以把group和item进行组合来组合出一个新的group。

可以通过下面的方式创建组头组尾以及补充视图:

//配置装饰Badge
//装饰视图中心点等于cell的右上角顶点
NSCollectionLayoutAnchor *badgeAnchor = [NSCollectionLayoutAnchor layoutAnchorWithEdges:NSDirectionalRectEdgeTop|NSDirectionalRectEdgeTrailing fractionalOffset:CGPointMake(0.5, -0.5)];
NSCollectionLayoutSize *badgeSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:20] heightDimension:[NSCollectionLayoutDimension absoluteDimension:20]];
NSCollectionLayoutSupplementaryItem *badge = [NSCollectionLayoutSupplementaryItem supplementaryItemWithLayoutSize:badgeSize elementKind:@"Badge" containerAnchor:badgeAnchor];

//顶部大item
NSCollectionLayoutSize *topItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalWidthDimension:9.0/16.0]];
NSCollectionLayoutItem *topItem = [NSCollectionLayoutItem itemWithLayoutSize:topItemSize];

//底部小item 通过supplementaryItems属性指定装饰badge视图
NSCollectionLayoutSize *bottomItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.5] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
NSCollectionLayoutItem *bottomItem = [NSCollectionLayoutItem itemWithLayoutSize:bottomItemSize supplementaryItems:@[badge]];
bottomItem.contentInsets = NSDirectionalEdgeInsetsMake(8, 8, 8, 8);

//底部放两个小item对应的group
NSCollectionLayoutSize *bottomGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.5]];
//注意:该api放两个相同的item是不起效的
//NSCollectionLayoutGroup *bottomGroup = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:bottomGroupSize subitems:@[bottomItem, bottomItem]];
////会在这个分组中放入两个相同的item。并且这里设置了2,即使bottomItemSize width设置比较大,依然会平分
NSCollectionLayoutGroup *bottomGroup = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:bottomGroupSize subitem:bottomItem count:2];

//组合group  注意这里的尺寸一点要是组合的大小
NSCollectionLayoutSize *fullGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalWidthDimension:9.0/16.0 + 0.5]];
NSCollectionLayoutGroup *nestedGroup = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:fullGroupSize subitems:@[topItem, bottomGroup]];

//虽然一个section只能指定一个group,但可以group中可以组合item和其他group
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:nestedGroup];
    
NSCollectionLayoutSize *headerSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension estimatedDimension:44]];
NSCollectionLayoutBoundarySupplementaryItem *headerItem = [NSCollectionLayoutBoundarySupplementaryItem boundarySupplementaryItemWithLayoutSize:headerSize elementKind:UICollectionElementKindSectionHeader alignment:NSRectAlignmentTop];
//是否悬停
//headerItem.pinToVisibleBounds = YES;
    
//配置组头组尾
NSCollectionLayoutSize *footerSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension estimatedDimension:60]];
NSCollectionLayoutBoundarySupplementaryItem *footerItem = [NSCollectionLayoutBoundarySupplementaryItem boundarySupplementaryItemWithLayoutSize:footerSize elementKind:UICollectionElementKindSectionFooter alignment:NSRectAlignmentBottom];

//给section的boundarySupplementaryItems属性赋值
section.boundarySupplementaryItems = @[headerItem, footerItem];

通过这个demo,我们还可以看出UICollectionView的numberOfSections和NSCollectionLayoutSection的关系。这里我第一个分区的numberOfSections=2,numberOfRows=8,每个NSCollectionLayoutSection可以放下三个cell,实际效果是会按这个NSCollectionLayoutSection样式依次布局cell,但每个numberOfSections对应的分区开始的时候一定是从一个新的NSCollectionLayoutSection布局开始的,我们看下两个分区的交界处即可理解:

分区交界处

用装饰视图做背景,实现类似UITableViewStyleGrouped样式

类似UITableViewStyleGrouped样式

//group加装饰背景
1.创建NSCollectionLayoutDecorationItem 它是NSCollectionLayoutItem的子类,不能指定layoutSize。
2.给NSCollectionLayoutSection的decorationItems赋值。
3.在UICollectionViewCompositionalLayout上面注册decorationView,在这里注册的类实现样式的自定义。
注意:不是在collectionview上注册SupplementaryView。

NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];

NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension absoluteDimension:44]];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:groupSize subitems:@[item]];

NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
section.contentInsets = NSDirectionalEdgeInsetsMake(20, 20, 20, 20);

NSCollectionLayoutDecorationItem *backItem = [NSCollectionLayoutDecorationItem backgroundDecorationItemWithElementKind:@"background"];
backItem.contentInsets = NSDirectionalEdgeInsetsMake(10, 10, 10, 10);
section.decorationItems = @[backItem];

UICollectionViewCompositionalLayoutConfiguration *config = [UICollectionViewCompositionalLayoutConfiguration new];
config.interSectionSpacing = 20;//不同section之间的间距

UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc]initWithSection:section configuration:config];
[layout registerClass:[MyDecorateView class] forDecorationViewOfKind:@"background"];

自定义NSCollectionLayoutGroup

上面的demo都是水平或垂直的group样式,系统也提供了自定义group样式的api,这里只是简单举个例子。

自定义NSCollectionLayoutGroup
NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension absoluteDimension:200]];

CGFloat width = [UIScreen mainScreen].bounds.size.width/3.0;
CGFloat height = 200/3.0;
//无法横向滚动
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup customGroupWithLayoutSize:groupSize itemProvider:^NSArray * _Nonnull(id  _Nonnull layoutEnvironment) {
    //UICollectionView的每个section走一次
    //这里想象空间很大,自己爱咋放咋放
    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:8];
    CGFloat x = 0 , y = 0;
    for(NSInteger i = 0; i < 8; i++){
        NSCollectionLayoutGroupCustomItem *customItem = [NSCollectionLayoutGroupCustomItem customItemWithFrame:CGRectMake(x, y, width, height) zIndex:1000+i];
        [arr addObject:customItem];
        x += width;
        if(i > 0 && i % 3 == 0){
            x = 0;
            y += height;
        }
    }
    return arr.copy;
}];

综合实现类似AppStore tab页的效果

结合上面的例子,同时再实现为UICollectionView的每个分区都单独配置一个不同的section样式来实现目标效果。

AppStore布局效果

其中知识点:

  • 如何监测横向滚动的group当前滚动到了哪个cell?
    section的visibleItemsInvalidationHandler属性

  • 正交轴上滚动模式
    UICollectionLayoutSectionOrthogonalScrollingBehaviorContinuous 正常连续滚动
    UICollectionLayoutSectionOrthogonalScrollingBehaviorPaging 正常分页轮播(页宽等于UICollectionView的宽度)
    UICollectionLayoutSectionOrthogonalScrollingBehaviorContinuousGroupLeadingBoundary 停止滚动时一定会停留在group的边界处
    UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging 按group的尺寸进行分页
    UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPagingCentered 也是按group的尺寸进行分页,但会沿正交轴增加首尾间距让group居中

UICollectionViewCompositionalLayoutConfiguration *config = [UICollectionViewCompositionalLayoutConfiguration new];
config.interSectionSpacing = 30;//不同section之间的间距
    
    
UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc]initWithSectionProvider:^NSCollectionLayoutSection * _Nullable(NSInteger section, id environment) {
     return [self generateSectionForSection:section];
}];
layout.configuration = config;


- (NSCollectionLayoutSection *)generateSectionForSection:(NSInteger)section
{
    NSCollectionLayoutGroup *group;
    NSDirectionalEdgeInsets sectionInsets = NSDirectionalEdgeInsetsMake(0, 0, 0, 0);
    UICollectionLayoutSectionOrthogonalScrollingBehavior behavior = UICollectionLayoutSectionOrthogonalScrollingBehaviorContinuous;
    switch (section) {
        case 0:
        case 5:
        {
            //顶部banner item
            //效果:paging by cell 看起来cell距离屏幕左右各20像素
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 15, 0, 15);
            behavior = UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging;//完美的paging by cell效果
            
            CGFloat bannerSize = [UIScreen mainScreen].bounds.size.width - 30;//原本占screenwidth-30
            NSCollectionLayoutSize *bannerItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
            NSCollectionLayoutItem *bannerItem = [NSCollectionLayoutItem itemWithLayoutSize:bannerItemSize];
            bannerItem.contentInsets = NSDirectionalEdgeInsetsMake(0, 5, 0, 5);//左右各收缩5像素 占screenwidth-40

            NSCollectionLayoutSize *bannerGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:bannerSize] heightDimension:[NSCollectionLayoutDimension absoluteDimension:bannerSize]];
            group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:bannerGroupSize subitem:bannerItem count:1];//水平
            break;
        }
        case 1:
        case 2:
        case 4:
        case 7:
        case 9:
        case 10:
        {
            //每列三行的cell样式
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 15, 0, 15);
            behavior = UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging;//完美的paging by cell效果

            CGFloat oneThirdWidth = [UIScreen mainScreen].bounds.size.width - 30;
            CGFloat oneThirdHeight = 80;
            NSCollectionLayoutSize *oneThirdItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0/3.0]];
            NSCollectionLayoutItem *oneThirdItem = [NSCollectionLayoutItem itemWithLayoutSize:oneThirdItemSize];
            oneThirdItem.contentInsets = NSDirectionalEdgeInsetsMake(0, 5, 0, 5);

            NSCollectionLayoutSize *oneThirdGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:oneThirdWidth] heightDimension:[NSCollectionLayoutDimension absoluteDimension:oneThirdHeight * 3.0]];
            group = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:oneThirdGroupSize subitem:oneThirdItem count:3];//垂直
            
            break;
        }
        case 3:
        {
            //今天看什么banner
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 15, 0, 15);
            behavior = UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging;//完美的paging by cell效果

            CGFloat bannerWidth = [UIScreen mainScreen].bounds.size.width - 150;
            CGFloat bannerHeight = 180;
            NSCollectionLayoutSize *bannerItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
            NSCollectionLayoutItem *bannerItem = [NSCollectionLayoutItem itemWithLayoutSize:bannerItemSize];
            bannerItem.contentInsets = NSDirectionalEdgeInsetsMake(0, 5, 0, 5);

            NSCollectionLayoutSize *bannerGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:bannerWidth] heightDimension:[NSCollectionLayoutDimension absoluteDimension:bannerHeight]];
            group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:bannerGroupSize subitem:bannerItem count:1];//水平
            
            break;
        }
        case 6:
        {
            //热门类别
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 20, 0, 20);

            CGFloat cellWidth = [UIScreen mainScreen].bounds.size.width - 40;
            CGFloat cellHeight = 50;
            NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
            NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];

            NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:cellWidth] heightDimension:[NSCollectionLayoutDimension absoluteDimension:cellHeight * 8]];
            group = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:groupSize subitem:item count:8];//垂直
            break;
        }
        case 8:
        {
            //每列两行的cell样式
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 15, 0, 15);
            behavior = UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging;//完美的paging by cell效果
            
            CGFloat oneSecondWidth = [UIScreen mainScreen].bounds.size.width - 30;
            CGFloat oneSecondHeight = 120;
            NSCollectionLayoutSize *oneSecondItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0/2.0]];
            NSCollectionLayoutItem *oneSecondItem = [NSCollectionLayoutItem itemWithLayoutSize:oneSecondItemSize];
            oneSecondItem.contentInsets = NSDirectionalEdgeInsetsMake(0, 5, 0, 5);

            NSCollectionLayoutSize *oneSecondGroupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:oneSecondWidth] heightDimension:[NSCollectionLayoutDimension absoluteDimension:oneSecondHeight * 2.0]];
            group = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:oneSecondGroupSize subitem:oneSecondItem count:2];//垂直
            break;
        }
        case 11:
        {
            //快速链接
            sectionInsets = NSDirectionalEdgeInsetsMake(0, 20, 0, 20);

            CGFloat cellWidth = [UIScreen mainScreen].bounds.size.width - 40;
            CGFloat cellHeight = 50;
            NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1.0]];
            NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];

            NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension absoluteDimension:cellWidth] heightDimension:[NSCollectionLayoutDimension absoluteDimension:cellHeight * 8]];
            group = [NSCollectionLayoutGroup verticalGroupWithLayoutSize:groupSize subitem:item count:8];//垂直
            break;
            break;
        }
    }
    
    NSCollectionLayoutSection *layoutSection = [NSCollectionLayoutSection sectionWithGroup:group];
    layoutSection.orthogonalScrollingBehavior = behavior;
    layoutSection.contentInsets = sectionInsets;
    if(section == 0){
        layoutSection.visibleItemsInvalidationHandler = ^(NSArray> * _Nonnull visibleItems, CGPoint contentOffset, id  _Nonnull layoutEnvironment) {
            //这里可以判断哪个cell滚动到了屏幕中心
        };
    }
    
    //配置组头组尾
    NSCollectionLayoutSize *headerSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1.0] heightDimension:[NSCollectionLayoutDimension estimatedDimension:44]];
    NSCollectionLayoutBoundarySupplementaryItem *headerItem = [NSCollectionLayoutBoundarySupplementaryItem boundarySupplementaryItemWithLayoutSize:headerSize elementKind:UICollectionElementKindSectionHeader alignment:NSRectAlignmentTop];
    
    layoutSection.boundarySupplementaryItems = @[headerItem];
    
    return layoutSection;
}

总结

UICollectionViewCompositionalLayout是继UICollectionViewFlowLayout之后,苹果公司为开发者提供的一个更加灵活功能更加强大的一种布局方案。这是顺应时代的产物,是iOS开发者的开发利器,可以很大程度上提升开发效率,用更少的时间完成更复杂的布局效果。除了API越来与长外,其他没什么毛病。

源码已放至Github: ATPagingByCell

你可能感兴趣的:(初探UICollectionViewCompositionalLayout)