初探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<NSCollectionLayoutGroupCustomItem *> * _Nonnull(id<NSCollectionLayoutEnvironment>  _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<NSCollectionLayoutEnvironment> 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<id<NSCollectionLayoutVisibleItem>> * _Nonnull visibleItems, CGPoint contentOffset, id<NSCollectionLayoutEnvironment>  _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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349