一、完成目标
当咱们要完成App store
的游戏页面的时分,惯性思想或许便是咱们需求树立一个UITableView
,并且在tableHeaderView
或许在第一个cell
内部嵌套一个横向滑动的UICollectionView
。
其实咱们能够直接用一个collectionView就能够完成这么一个作用。这便是今天的主角——UICollectionViewCompositionalLayout
。
二、层级结构
由图可见,层级关系为 NSCollectionLayoutItem -> NSCollectionLayoutGroup -> NSCollectionLayoutSection
由图片可知,相对于本来的layout,多了一个Group特点。
三、先了解一下前期需求的装备
1.NSCollectionLayoutSize
咱们先看看怎么去声明一个NSCollectionLayoutSize
//1.装备NSCollectionLayoutSize
NSCollectionLayoutDimension *itemSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:0.25];
NSCollectionLayoutDimension *itemSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:1.0];
NSCollectionLayoutSize *fractionalItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:itemSizeWidthDimension
heightDimension:itemSizeHeightDimension];
在这个函数中咱们需求传入两个NSCollectionLayoutDimension
对象,去别离操控Item的宽和高。咱们点进去能够看到
+ (instancetype)fractionalWidthDimension:(CGFloat)fractionalWidth;
+ (instancetype)fractionalHeightDimension:(CGFloat)fractionalHeight;
+ (instancetype)absoluteDimension:(CGFloat)absoluteDimension;
+ (instancetype)estimatedDimension:(CGFloat)estimatedDimension;
相对于本来的UICollectionViewFlowLayout
的界说办法。NSCollectionLayoutDimension
供给了多种声明函数来界说。
- fractionalWidthDimension && fractionalHeightDimension
fractional
是关键词,能够理解为一个相对布局,后面传入的是一个相对于父视图的一个比例值; - absoluteDimension
absolute
是关键词,能够理解为咱们写frame的数值,你写了多少显示的便是多少; - estimatedDimension
estimated
这个单词咱们再写UITableView
的时分比较常见,顾名思义便是你能够给一个预估值;
2.NSCollectionLayoutItem
这儿对象初始化函数中需求带上咱们方才生成的itemSize。
//2.装备NSCollectionLayoutItem
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:fractionalItemSize];
3.NSCollectionLayoutGroup
一共供给了5个初始化的办法,主要分为3大类。
- 水平
- 笔直
- 自界说
其中水平缓笔直方向都供给了两种初始化的办法,主要的差别是多了一个count参数。
如果是自界说布局,需求传入一个NSCollectionLayoutGroupCustomItemProvider来决议这个 Group中Item的布局办法。经过Group能够在同一个Section中完成不同的布局办法。\
+ (instancetype)horizontalGroupWithLayoutSize:(NSCollectionLayoutSize*)layoutSize
repeatingSubitem:(NSCollectionLayoutItem*)subitem
count:(NSInteger)count;
+ (instancetype)horizontalGroupWithLayoutSize:(NSCollectionLayoutSize*)layoutSize
subitems:(NSArray<NSCollectionLayoutItem*>*)subitems;
+ (instancetype)verticalGroupWithLayoutSize:(NSCollectionLayoutSize*)layoutSize
repeatingSubitem:(NSCollectionLayoutItem*)subitem
count:(NSInteger)count;
+ (instancetype)verticalGroupWithLayoutSize:(NSCollectionLayoutSize*)layoutSize
subitems:(NSArray<NSCollectionLayoutItem*>*)subitems;
+ (instancetype)customGroupWithLayoutSize:(NSCollectionLayoutSize*)layoutSize
itemProvider:(NSCollectionLayoutGroupCustomItemProvider)itemProvider;
需求留意的是:
这儿初始化函数的第一个参数layoutSize
需求从头声明一个size对象,不能运用咱们第一步的那个size。
//3.装备NSCollectionLayoutGroup
NSCollectionLayoutDimension *gropSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:1.0];
NSCollectionLayoutDimension *gropSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:0.1];
NSCollectionLayoutSize *gropSize = [NSCollectionLayoutSize sizeWithWidthDimension:gropSizeWidthDimension
heightDimension:gropSizeHeightDimension];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:gropSize
subitems:@[item]];
4.NSCollectionLayoutSection
初始化的时分带上之前界说好的group就能够。
//4.装备NSCollectionLayoutSection
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
5.UICollectionViewCompositionalLayout
初始化的时分把咱们之前声明好的section
带入就能够
//5.装备UICollectionViewCompositionalLayout
UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc] initWithSection:section];
但是这样的声明办法只能声明一个section。
四、探索实际的运用
1.事例一:完成一个最简单的CompositionalLayout
上一个部分现已介绍了怎么去完成一个CompositionalLayout,那么咱们接下来就要把咱们设计的布局放到CollectionView中去完成。只需咱们在CollectionView的声明函数中代入咱们的Layout或许单独的用collectionView.collectionViewLayout = layout;
来完成就行。剩余的及时要去完成基本的署理办法就能够
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//1.装备NSCollectionLayoutSize
NSCollectionLayoutDimension *itemSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:0.25];
NSCollectionLayoutDimension *itemSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:1.0];
NSCollectionLayoutSize *fractionalItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:itemSizeWidthDimension
heightDimension:itemSizeHeightDimension];
//2.装备NSCollectionLayoutItem
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:fractionalItemSize];
//3.装备NSCollectionLayoutGroup
NSCollectionLayoutDimension *gropSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:1.0];
NSCollectionLayoutDimension *gropSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:0.1];
NSCollectionLayoutSize *gropSize = [NSCollectionLayoutSize sizeWithWidthDimension:gropSizeWidthDimension
heightDimension:gropSizeHeightDimension];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:gropSize
subitems:@[item]];
//4.装备NSCollectionLayoutSection
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
//5.装备UICollectionViewCompositionalLayout
UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc] initWithSection:section];
UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
collectionView.delegate = self;
collectionView.dataSource = self;
[collectionView registerClass:[EazyCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([EazyCollectionViewCell class])];
[self.view addSubview:collectionView];
}
#pragma mark - <UICollectionViewDelegate,UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 2;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 8;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
EazyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([EazyCollectionViewCell class]) forIndexPath:indexPath];
if (indexPath.section ==0) {
cell.backgroundColor = [UIColor redColor];
} else {
cell.backgroundColor = [UIColor yellowColor];
}
cell.titleLabel.text = [NSString stringWithFormat:@"%ld---%ld",(long)indexPath.section,(long)indexPath.item];
return cell;
}
这便是咱们完成的作用,直观的感觉是不是感觉和之前的Layout没什么区别,这么写反而愈加的复杂。
这样的原因主要是由于咱们的UICollectionViewCompositionalLayout
的声明办法只绑定了一种NSCollectionLayoutSection
。
2.事例二:让UICollectionViewCompositionalLayout绑定多个section
让咱们再次的点进UICollectionViewCompositionalLayout
内部,咱们能看到这么一个函数
- (instancetype)initWithSectionProvider:(UICollectionViewCompositionalLayoutSectionProvider)sectionProvider;
咱们能够依据block中的sectionIndex
特点来判别是哪个section
然后提早布局
UICollectionViewCompositionalLayout *layout = [[UICollectionViewCompositionalLayout alloc] initWithSectionProvider:^NSCollectionLayoutSection * _Nullable(NSInteger sectionIndex, id<NSCollectionLayoutEnvironment> _Nonnull layoutEnvironment) {
if (sectionIndex == 0) {
NSCollectionLayoutDimension *itemSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:0.25];
NSCollectionLayoutDimension *itemSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:1.0];
NSCollectionLayoutSize *fractionalItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:itemSizeWidthDimension
heightDimension:itemSizeHeightDimension];
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:fractionalItemSize];
NSCollectionLayoutDimension *gropSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:1.0];
NSCollectionLayoutDimension *gropSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:0.1];
NSCollectionLayoutSize *gropSize = [NSCollectionLayoutSize sizeWithWidthDimension:gropSizeWidthDimension
heightDimension:gropSizeHeightDimension];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:gropSize
subitems:@[item]];
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
return section;
} else {
NSCollectionLayoutDimension *itemSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:0.5];
NSCollectionLayoutDimension *itemSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:1.0];
NSCollectionLayoutSize *fractionalItemSize = [NSCollectionLayoutSize sizeWithWidthDimension:itemSizeWidthDimension
heightDimension:itemSizeHeightDimension];
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:fractionalItemSize];
NSCollectionLayoutDimension *gropSizeWidthDimension = [NSCollectionLayoutDimension fractionalWidthDimension:1.0];
NSCollectionLayoutDimension *gropSizeHeightDimension = [NSCollectionLayoutDimension fractionalHeightDimension:0.2];
NSCollectionLayoutSize *gropSize = [NSCollectionLayoutSize sizeWithWidthDimension:gropSizeWidthDimension
heightDimension:gropSizeHeightDimension];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:gropSize
subitems:@[item]];
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
return section;
}
}];
3.进阶特点设置
3.1 距离
UICollectionViewFlowLayout
设置距离特点只需设置minimumLineSpacing
和minimumInteritemSpacing
就能够。
但是在UICollectionViewCompositionalLayout
里面我暂时并没有发现这两个特点。这儿设置距离主要分为三种:
- item和item之间
- group和group之间
- section和section之间
而设置距离的办法有两种办法:
- contentInsets
- spacing
3.1.1 ContentInsets
Item、Group 和 Section 都有一个特点 contentInsets 用于设置边距。
3.1.1.1 item.contentInsets
- 当给 Item 设置
contentInsets
后的示意图:
灰色区域是 Item,赤色框是 Item 的鸿沟,赤色的上下左右边距便是设置的 contentInsets
- 设置语法
item.contentInsets = NSDirectionalEdgeInsetsMake(5, 5, 5, 5);
3.1.1.2 group.contentInsets
- 当给 Group 设置
contentInsets
后的示意图:
灰色区域是 Item,赤色框是 Item 的鸿沟,蓝色框是 Group 的鸿沟,蓝色的上下左右边距便是设置的 contentInsets。 - 设置语法
group.contentInsets = NSDirectionalEdgeInsetsMake(5, 5, 5, 5);
3.1.1.3 section.contentInsets
-
当给 Section 设置
contentInsets
后的示意图:灰色区域是 Item,赤色框是 Item 的鸿沟,蓝色框是 Group 的鸿沟,绿色框是 Section 的鸿沟,绿色的上下左右边距便是设置的 contentInsets。
-
设置语法
group.contentInsets = NSDirectionalEdgeInsetsMake(5, 5, 5, 5);
留意须知
为了使整体的上下左右边距相同,通常需求同时设置 Item 和 Group 的contentInsets
。
3.2 Spacing
能够直接给 Group 和 Section 设置相应的 Spacing 以到达设置 Item 和 Group 之间距离的目的,但这种需求准确核算距离的值,由于距离会挤占 Item 和 Group 的空间。
group.interItemSpacing = [NSCollectionLayoutSpacing fixedSpacing:8];
group.edgeSpacing = [NSCollectionLayoutEdgeSpacing spacingForLeading:[NSCollectionLayoutSpacing fixedSpacing:8] top:[NSCollectionLayoutSpacing fixedSpacing:8] trailing:[NSCollectionLayoutSpacing fixedSpacing:8] bottom:[NSCollectionLayoutSpacing fixedSpacing:8]];
section.interItemSpacing = [NSCollectionLayoutSpacing fixedSpacing:8];
经过试验得知: 当咱们给group设置特点的时分,作用是来完成item之间的spacing; 当给section设置特点的时分,作用是来完成group之间的spacing;
3.3 翻滚办法
typedef NS_ENUM(NSInteger,UICollectionLayoutSectionOrthogonalScrollingBehavior) {
// default behavior. Section will layout along main layout axis (i.e. configuration.scrollDirection)
//默认行为。Section将沿着主布局轴(即configuration.scrollDirection)进行布局。
UICollectionLayoutSectionOrthogonalScrollingBehaviorNone,
// NOTE: For each of the remaining cases, the section content will layout orthogonal to the main layout axis (e.g. main layout axis == .vertical, section will scroll in .horizontal axis)
// Standard scroll view behavior: UIScrollViewDecelerationRateNormal
//留意:对于其余每种状况,section内容的布局将与主布局轴正交(例如,主布局轴== .笔直,section将在。水平轴上翻滚)
//翻滚视图的标准行为:uiscrollviewdedeerationratnormal
UICollectionLayoutSectionOrthogonalScrollingBehaviorContinuous,
// Scrolling will come to rest on the leading edge of a group boundary
//翻滚将停在组鸿沟的前缘
UICollectionLayoutSectionOrthogonalScrollingBehaviorContinuousGroupLeadingBoundary,
// Standard scroll view paging behavior (UIScrollViewDecelerationRateFast) with page size == extent of the collection view's bounds
//标准翻滚视图分页行为(uiscrollviewdedeerationratefast),页面巨细==集合视图鸿沟的规模
UICollectionLayoutSectionOrthogonalScrollingBehaviorPaging,
// Fractional size paging behavior determined by the sections layout group's dimension
//分段巨细的分页行为由section布局组的维度决议
UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPaging,
// Same of group paging with additional leading and trailing content insets to center each group's contents along the orthogonal axis
//与组分页相同,添加了额外的前导和尾内容刺进,以使每个组的内容沿正交轴居中
UICollectionLayoutSectionOrthogonalScrollingBehaviorGroupPagingCentered,
}
体系给咱们供给了以上6种翻滚办法,咱们经过绑定多组不同的section的同时也能够给每个section设置不同的翻滚办法,这样就能够完成咱们这边文章的主题,
用一个布局来处理嵌套问题—— UICollectionViewCompositionalLayout
五、道谢
这次文章的撰写,我在网上也借鉴了很多优秀博主的文章:
- iOS开发之UICollectionViewCompositionalLayout
- CompositionalLayout_Demo
这两篇文章或许Demo中运用的都是Swift言语,虽然现在OC渐渐地现已不再那么火热,但是根据作业原因,运用的还是OC所以就萌生了写这么一篇文章的想法。也希望能在这个渠道多和各位技术大牛们交流学习。