【iOS】UICollectionView的装饰视图——decorationView
程序员文章站
2024-03-24 10:13:10
...
最近有一个新需求,需要给列表中每一个section单独加一个背景,如下:
这……用tableview做的想法在看到这个背景的时候就没了,人也傻了。百度了一下发现UICollectionView可以实现,就是使用decorationView,不过资料还蛮少的,都是13年左右,坑也有不少,自己先按着教程摸索着整了一个,记录一下。
先说一下流程:
- 创建一个继承自UICollectionReusableView的视图,这就是我们接下来要用的decorationView装饰视图,这里我命名为XY_RM_ListViewCollectionSectionDecorationView;
- 创建一个继承自UICollectionViewLayoutAttributes的类,这是装饰视图需要用到的Attributes,如果装饰视图需要根据不同的section设置不同的图片,就可以在这个类里添加属性来记录。我的需求中的背景很单一,就是一个view加了shadow,所以我使用的是UICollectionViewLayoutAttributes,并没有创建新类;
- 创建layout文件设置布局;
从第一步开始:创建一个继承自UICollectionReusableView的视图;
//初始化
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
//加载Attributes
-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
[super applyLayoutAttributes:layoutAttributes];
[self.bgView removeFromSuperview];
[self addSubview:self.bgView];
}
要调用apply方法来加载attributes,在这里设置你需要设置的内容,要注意decorationView会重用;
第二步我没有做,就不写了
第三步创建布局文件:
@protocol XY_RM_ListViewCollectionLayoutDelegate <NSObject>
//itemsize
-(CGSize)sizeOfItemForIndexPath:(NSIndexPath *)indexPath;
//表头高度
-(CGFloat)heightOfHeaderViewForIndexPath:(NSIndexPath *)indexPath;
@end
@interface XY_RM_ListViewCollectionLayout : UICollectionViewLayout
@property (nonatomic, weak) id<XY_RM_ListViewCollectionLayoutDelegate>delegate;
@property (nonatomic, strong) NSMutableArray *sectionSizeArray; ///<每个section位置的数组
@end
这里定义了两个协议方法来获取itemSize和header的高度,同时定义了一个sectionSizeArray,这个数组储存每一个section的frame数据。
//初始化
-(instancetype)init{
if (self = [super init]) {
//注册装饰视图
[self registerClass:[XY_RM_ListViewCollectionSectionDecorationView class] forDecorationViewOfKind:@"XY_RM_ListViewCollectionSectionDecorationView"];
}
return self;
}
decorationView比较特殊,是要在layout里面进行注册的,这里我们放在init里注册。
//开始布局
-(void)prepareLayout{
//初始化
self.totalHeight = 0.0;
self.attArray = [NSMutableArray array];
self.sectionSizeArray = [NSMutableArray array];
//遍历section
NSInteger sectionCount = [self.collectionView numberOfSections];
for (int i=0; i<sectionCount; i++) {
NSIndexPath *sectionIndexPath = [NSIndexPath indexPathWithIndex:i];
//设置起点
CGPoint startPoint = CGPointMake(0, self.totalHeight);
//设置上内边距
self.totalHeight += self.topSpace;
//如果有高度,添加头部属性
CGFloat height=0.0;
if ([self.delegate respondsToSelector:@selector(heightOfHeaderViewForIndexPath:)]) {
height = [self.delegate heightOfHeaderViewForIndexPath: sectionIndexPath];
}
if (height>0.0) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:sectionIndexPath];
[self.attArray addObject:attributes];
}
//头部添加完添加当前section中item的属性
NSInteger itemsCount = [self.collectionView numberOfItemsInSection:i];
self.x = 0.0;
self.y = self.totalHeight;
for (int j=0; j<itemsCount; j++) {
NSIndexPath *itemIndexPath = [NSIndexPath indexPathForItem:j inSection:i];
//添加item的属性
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
[self.attArray addObject:attributes];
//如果是这个section最后一个,修改total
if (j == itemsCount-1) {
self.totalHeight = CGRectGetMaxY(attributes.frame)+self.bottomSpace;
//保存终点
CGPoint endPoint = CGPointMake(CGRectGetMaxX(attributes.frame), self.totalHeight);
//设置section的frame
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x-startPoint.x, endPoint.y-startPoint.y);
//保存
[self.sectionSizeArray addObject:NSStringFromCGRect(rect)];
//添加装饰att
UICollectionViewLayoutAttributes *attri = [self layoutAttributesForDecorationViewOfKind:@"" atIndexPath:sectionIndexPath];
[self.attArray addObject:attri];
}
}
}
}
计算sectionframe我是使用point的方式,在header之前记录startpoint,在section中最后一个item之后记录endpoint,section的size就是两点相减,将rectString添加进sectionSizeArray;
-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{
//获取属性
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"XY_RM_ListViewCollectionSectionDecorationView" withIndexPath:indexPath];
//设置frame
NSString *rectString = [self.sectionSizeArray objectAtIndex: indexPath.section];
CGRect frame = CGRectFromString(rectString);
attributes.frame = frame;
//纵向坐标调整到底下
attributes.zIndex = -1;
return attributes;
}
zIndex这个属性调整collectionview中的层级关系,cell是0,要想装饰视图在cell底部,就要把装饰视图的zindex调整到小于0;
到目前为止就ok了,运行就能看见上图效果