项目中遇到要实现的效果
image.png

经过查阅资料,最终实现效果

实现的原理如下:

  1. 自定义并注册复用视图 ``` @property(class, nonatomic, readonly) Class layoutAttributesClass; // override this method to provide a custom class to be used when instantiating instances of UICollectionViewLayoutAttributes
  • (void)registerClass:(nullable Class)viewClass forDecorationViewOfKind:(NSString *)elementKind; ```
  1. 在layoutAttributesForElementsInRect方法中添加自定义的UICollectionViewLayoutAttributes,并设置LayoutAttributes的Frame(Frame的计算方式可以按照自己的需求做修改)

    注意创建时需要使用自定义的UICollectionViewLayoutAttributes调用如下的方法以使其显示时使用自定义的UICollectionReusableView

  1. + (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;

其实是可以对每一个item进行背景色处理的,主需按照需求在**layoutAttributesForElementsInRect方法中添加对应的**LayoutAttributes就可以了

实现思路

  1. 自定义UICollectionViewLayoutAttributes
    1. 添加一个自定义的属性 ```

      import

NS_ASSUME_NONNULL_BEGIN @class TFSectionDecorationConfig; @interface TFCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes @property (nonatomic, strong) TFSectionDecorationConfig *decorationConfig; @end

NS_ASSUME_NONNULL_END

  1. ```
  2. #import <UIKit/UIKit.h>
  3. NS_ASSUME_NONNULL_BEGIN
  4. @interface TFSectionDecorationConfig : NSObject
  5. /// 参考Section的inset,在这里可以做适当的修改
  6. @property (nonatomic, assign) UIEdgeInsets inset;
  7. /// 背景色
  8. @property (nonatomic, strong) UIColor *color;
  9. /// 圆角
  10. @property (nonatomic, assign) CGFloat cornerRadius;
  11. @end
  12. NS_ASSUME_NONNULL_END
  1. 自定义UICollectionReusableView ```

    import

NS_ASSUME_NONNULL_BEGIN

@interface TFCollectionReusableView : UICollectionReusableView

@end

NS_ASSUME_NONNULL_END

import “TFCollectionReusableView.h”

import “TFCollectionViewLayoutAttributes.h”

import “TFSectionDecorationConfig.h”

@implementation TFCollectionReusableView

  • (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { [super applyLayoutAttributes:layoutAttributes];

    TFCollectionViewLayoutAttributes ecLayoutAttributes = (TFCollectionViewLayoutAttributes)layoutAttributes; TFSectionDecorationConfig *config = ecLayoutAttributes.decorationConfig; if (config == nil) {

    1. self.backgroundColor = [UIColor clearColor];
    2. return;

    } self.backgroundColor = config.color; self.layer.cornerRadius = config.cornerRadius; self.layer.masksToBounds = YES; }

@end

  1. 3. 自定义UICollectionViewFlowLayout
  2. 1. 替换为自定义的UICollectionViewLayoutAttributes
  3. 1. 注册自定义的DecorationView
  4. 1. layoutAttributesForElementsInRect方法中,计算并添加自定义的UICollectionViewLayoutAttributes

import

NS_ASSUME_NONNULL_BEGIN @class TFSectionDecorationConfig; @class TFCollectionViewLayout; @protocol TFCollectionViewLayoutDelegate

/// 返回分组背景视图配置 /// @param layout layout description /// @param section section description

  • (TFSectionDecorationConfig _Nullable)tf_Layout:(TFCollectionViewLayout )layout decorationConfigForSectionAtIndex:(NSInteger)section;

@end

@interface TFCollectionViewLayout : UICollectionViewFlowLayout

@property (nonatomic, weak) id delegate;

@end

NS_ASSUME_NONNULL_END

import “TFCollectionViewLayout.h”

import “TFCollectionViewLayoutAttributes.h”

import “TFCollectionReusableView.h”

import “TFSectionDecorationConfig.h”

static NSString *TFSectionDecorationReuseID = @”SectionBg”;

@implementation TFCollectionViewLayout

/// 替换为自定义的LayoutAttributes类

  • (Class)layoutAttributesClass { return [TFCollectionViewLayoutAttributes class]; }

/// 注册自定义的ReusableView,用于分组背景色显示

  • (void)prepareLayout { [super prepareLayout]; [self registerClass:[TFCollectionReusableView class] forDecorationViewOfKind:TFSectionDecorationReuseID]; }
  • (NSArray )layoutAttributesForElementsInRect:(CGRect)rect { NSArray attributes = [super layoutAttributesForElementsInRect:rect];

    NSMutableArray *allAttributes = [NSMutableArray arrayWithArray:attributes];

  1. NSMutableArray *sectionAttributes = [NSMutableArray array];
  2. NSInteger sections = self.collectionView.numberOfSections;
  3. for (NSInteger section = 0; section<sections; section++) {
  4. TFSectionDecorationConfig *config;
  5. if (self.delegate && [self.delegate respondsToSelector:@selector(tf_Layout:decorationConfigForSectionAtIndex:)]) {
  6. config = [self.delegate tf_Layout:self decorationConfigForSectionAtIndex:section];
  7. }else{
  8. continue;
  9. }
  10. NSInteger number = [self.collectionView numberOfItemsInSection:section];
  11. UIEdgeInsets insets = config.inset;
  12. if(number > 0){
  13. NSIndexPath *firstIndex = [NSIndexPath indexPathForRow:0 inSection:section];
  14. UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:firstIndex];
  15. CGRect frame = attribute.frame;
  16. CGFloat minX = frame.origin.x;
  17. CGFloat minY = frame.origin.y;
  18. CGFloat maxX = CGRectGetMaxX(frame);
  19. CGFloat maxY = CGRectGetMaxY(frame);
  20. for (NSInteger row = 1; row<number; row++) {
  21. attribute = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];
  22. if (attribute.frame.origin.x < minX ) {
  23. minX = attribute.frame.origin.x;
  24. }
  25. if (attribute.frame.origin.y < minY ) {
  26. minY = attribute.frame.origin.y;
  27. }
  28. if (CGRectGetMaxX(attribute.frame) > maxX ) {
  29. maxX = CGRectGetMaxX(attribute.frame);
  30. }
  31. if (CGRectGetMaxY(attribute.frame) > maxY ) {
  32. maxY = CGRectGetMaxY(attribute.frame);
  33. }
  34. }
  35. frame = CGRectMake(minX, minY, maxX - minX, maxY - minY);
  36. TFCollectionViewLayoutAttributes *decorationAttributes =
  37. [TFCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:TFSectionDecorationReuseID
  38. withIndexPath:attribute.indexPath];
  39. decorationAttributes.decorationConfig = config;
  40. CGFloat x = frame.origin.x - insets.left;
  41. CGFloat y = frame.origin.y - insets.top;
  42. CGFloat w = frame.size.width + insets.left + insets.right;
  43. CGFloat h = frame.size.height + insets.top + insets.bottom;
  44. decorationAttributes.frame = CGRectMake(x, y, w , h);
  45. decorationAttributes.zIndex = attribute.zIndex-1;
  46. [sectionAttributes addObject:decorationAttributes];
  47. }
  48. }
  49. [allAttributes addObjectsFromArray:sectionAttributes];
  50. return allAttributes;

} @end

  1. - Demo效果代码

import “ViewController.h”

import “TFCollectionReusableHeaderView.h”

import “TFCollectionViewLayout.h”

import “TFSectionDecorationConfig.h”

define kGGRandomColor [UIColor colorWithRed:random()%256/255.0 green:random()%256/255.0 blue:random()%256/255.0 alpha:1.0]

@interface ViewController () @property (nonatomic, strong) UICollectionView *collectionView; @end

@implementation ViewController

  • (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:self.collectionView];

    UIButton *refreshBtn = [UIButton new]; [self.view addSubview:refreshBtn]; [refreshBtn setTitle:@” 刷新 “ forState:UIControlStateNormal]; [refreshBtn addTarget:self action:@selector(btnDidClicked:) forControlEvents:UIControlEventTouchUpInside]; refreshBtn.frame = CGRectMake(0, 100, 50, 50);

}

  • (void)btnDidClicked:(UIButton *)sender{ [self.collectionView reloadData]; }
  • (UICollectionView *)collectionView{ if (!_collectionView) {

    1. TFCollectionViewLayout *flowLayout = [[TFCollectionViewLayout alloc] init];
    2. flowLayout.delegate = self;

    // UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];

    1. flowLayout.minimumLineSpacing = 5;
    2. UICollectionView *view = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
    3. view.dataSource = self;
    4. view.delegate = self;
    5. view.backgroundColor = [UIColor whiteColor];
    6. [view registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCell"];
    7. [view registerClass:[TFCollectionReusableHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
    8. _collectionView = view;

    } return _collectionView; }

pragma mark - UICollectionViewDataSource

  • (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return 5; }
  • (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{ return 10; }

// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:

  • (__kindof UICollectionViewCell )collectionView:(UICollectionView )collectionView cellForItemAtIndexPath:(NSIndexPath )indexPath{ UICollectionViewCell cell = [collectionView dequeueReusableCellWithReuseIdentifier:@”UICollectionViewCell” forIndexPath:indexPath]; cell.backgroundColor = [UIColor systemPinkColor]; return cell; }
  • (UICollectionReusableView )collectionView:(UICollectionView )collectionView viewForSupplementaryElementOfKind:(NSString )kind atIndexPath:(NSIndexPath )indexPath{ TFCollectionReusableHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@”header” forIndexPath:indexPath]; headerView.backgroundColor = [UIColor darkGrayColor];

    return headerView; }

  • (CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout )collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{

    CGSize size = collectionView.bounds.size; return CGSizeMake(size.width, 20); }

  • (CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout )collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath )indexPath{ CGSize itemSize = CGSizeMake(100, 100 + indexPath.row 20);

    return itemSize; }

  • (UIEdgeInsets)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout )collectionViewLayout insetForSectionAtIndex:(NSInteger)section{ if (section < 2) {

    1. return UIEdgeInsetsMake(0, 0, 0, 0);

    } return UIEdgeInsetsMake(10 + 5 section, 10, 20, section 4 + 5); }

pragma mark - TFCollectionViewLayoutDelegate

  • (TFSectionDecorationConfig _Nullable)tf_Layout:(TFCollectionViewLayout )layout decorationConfigForSectionAtIndex:(NSInteger)section{ if (section == 0) {
    1. return nil;
    } UIEdgeInsets sectionInset = [self collectionView:self.collectionView layout:layout insetForSectionAtIndex:section]; TFSectionDecorationConfig *config = [TFSectionDecorationConfig new]; config.color = kGGRandomColor; config.inset = UIEdgeInsetsMake(sectionInset.top, sectionInset.left - 5, sectionInset.bottom, sectionInset.right - 5); config.cornerRadius = 10; return config; }

@end

```

image.pngimage.png