Key points:

The example of Apple is to paste a background image on a cell.

Actually, a section with a background, that’s fine.

Apple is very flexible in their design. Basically, they can paste the background any way they want

Discovery in Practice

Second, set Decoration View, handwritten UICollectionViewFlowLayout (or UICollectionViewLayout), was to write dead.

Layout displays, typically with a network request. Before the data request comes back, go to the custom layout, go to the specific indexpath, access manual set up, because the actual does not exist, collapse.

Since no network requests data back, the actual number of sections is generally 0.

Need to judge.

Three, not Decoration View.

Do a commodity home page requirements, UICollectionView seven floors, each floor is not necessarily there, floor order is not necessarily.

If I write if else, it kills me. Through dictionary configuration, resolve the problem


Details:

Illustration:

“First cell”, which is the first section, has only one item

“Second cell”, which is the second section, has five items

One, a section, a background image

Set the area of the background image, paste it, end

Specific cooking tutorials are as follows:

Decorating views is a function of UICollectionViewLayout, not UICollectionView.

The methods of UICollectionView, the delegate methods (datasource), do not involve decorating the view.

UICollectionView doesn’t know anything about decorative views. UICollectionView renders according to UICollectionViewLayout Settings.

To use decorative views, you need to customize UICollectionViewLayout, which is a subclass of UICollectionViewLayout. This UICollectionViewLayout subclass allows you to add properties, proxy properties, and custom decorator views by setting proxy protocol methods.

The example used in this Demo is to add a decorative view background image.

(There is no reference to using proxies, setting protocol methods, and further controlling the decorator view)

In a nutshell, the custom Layout subclass implements a decorative view in five steps:

Step 1,

Have a Decoration View file.

I’m going to subclass UICollectionResuableView, and this is the concrete decorator view

@ interface FrontDecorationReusableView () / / decoration view, there is a picture @ property (nonatomic, strong) UIImageView * imageView; @end @implementation FrontDecorationReusableView - (instancetype)initWithFrame:(CGRect)frame{if(self = [super initWithFrame:frame]){ self.backgroundColor = UIColor.whiteColor; _imageView = [[UIImageView alloc] init]; [self addSubview: _imageView]; [imageview mas_makeConstraints:^(MASConstraintMaker *make) {make.edges. Mas_equalTo (self);}]; }return self;
}
Copy the code

Step 2,

Decorator views are registered in Layout.

With the decorative view, wire it up

In the custom Layout subclass, register the UICollectionResuableView subclass, which is the decorative view.

Call – (void)registerClass:(nullable Class)viewClass forDecorationViewOfKind:(NSString *)elementKind; Methods.

It is normally registered in the – (void)prepareLayout method.

- (void)prepareLayout {
    [super prepareLayout];
    [self registerClass: FrontDecorationReusableView.class forDecorationViewOfKind: FDRFrontDecorationReusableView];
}
Copy the code

Step 3,

Sets the location of the decorator view.

– (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind AtIndexPath :(NSIndexPath *)indexPath method that sets the position of the decorator view UICollectionResuableView because this method returns the layout properties of the decorator view.

+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath; Method to build layout properties and configure them accordingly.

First set the specific location of the decoration view,

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{
    
    if(elementKind == FDRFrontDecorationReusableView && indexPath.section == 1) { DecorationLayoutAttributes * attributes = [DecorationLayoutAttributes layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView withIndexPath: indexPath]; Attributes. ImgUrlStr = self.imgurLString; // Here, the location of the decorated view is fixed CGFloat heightOffset = 16; Attributes. Frame = CGRectMake(0, KScreenWidth * 0.5-Heightoffset, KScreenWidth, 102 + heightOffset); attributes.zIndex -= 1;return attributes;
    }
    return nil;
}
Copy the code

Step 4.

Rewritten – (NSArray < > UICollectionViewLayoutAttributes * *) layoutAttributesForElementsInRect (CGRect) the rect method, this method returns a given area, Layout properties for all views (grid view, supplementary view (header \ footer), decorative view).

Here to view is decorated on the paste, layoutAttributesForElementsInRect: array, the layout of the property is returned To include call – (UICollectionViewLayoutAttributes *) layoutAttributesForDecorationViewOfKind: (elementKind nsstrings *) AtIndexPath :(NSIndexPath *) layout property set in the indexPath method.

This is the key step, the collectionView gets enough information to display the decoration view. When layoutAttributesForElementsInRect: collectionView call, he will provide each kind of decorative view layout properties. The collectionView is isolated from the decorative view and knows nothing about it. The decoration view you see in the collectionView is provided by a custom layout.

In Step 2, the decorator view is registered, that is, a custom decorator view instance is created. The collectionView will be placed based on the layout properties.

Give the Decoration view layout properties set in the previous step to the collectionView for use

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{ NSArray<UICollectionViewLayoutAttributes *> * rawArr = [super layoutAttributesForElementsInRect: rect]; NSMutableArray<UICollectionViewLayoutAttributes *> * array = [[NSMutableArray alloc] initWithArray: rawArr]; NSInteger numberOfSections = [self.collectionView numberOfSections];if (numberOfSections == 0) {
        return rawArr;
    }
    UICollectionViewLayoutAttributes * decorationAttrs = [self layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView atIndexPath: [NSIndexPath indexPathForItem: 0 inSection: 1 ]];
    if (decorationAttrs && CGRectIntersectsRect(rect, decorationAttrs.frame)) {
        [array addObject: decorationAttrs];
    }
    return [array copy];
}


Copy the code

Step 5.

How do I pass values to decorated views?

Three steps:

CollcetionView -> Layout -> layoutAttributes -> decorationView Decorates the view

This article demo, is to configure the specific decoration picture.

Give your custom Layout an image address property,

@interface DecorationFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, copy) NSString * imgUrlString;
@end
Copy the code

Then find a way to send it, and you’re done

CollectionView sets the layout image URL and indirectly controls the image URL of the decoration view

. self.decorationFlowLayout.imgUrlString = @"https://fscdn.zto.com/GetPublicFile/ztPK4Y-WGgWKiRNfkygd3oYQ/thumbnail_747d31f481044bf6a149c7483cd097a5.jpg";
    [self.newMainCollectionView reloadData];
}
Copy the code

Custom layouts are also isolated from decorative views. Create a custom layout attribute object UICollectionViewLayoutAttributes to preach value, is to find a messenger.

Using UICollectionViewLayoutAttributes subclass, add attribute values.

@interface DecorationLayoutAttributes: UICollectionViewLayoutAttributes

@property (nonatomic, copy) NSString * imgUrlStr;
@end

Copy the code

LayoutAttributesForDecorationViewOfKind: configuration, is mentioned,

DecorationLayoutAttributes * attributes = [DecorationLayoutAttributes layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView withIndexPath: indexPath]; Attributes. ImgUrlStr = self.imgurLString;Copy the code

One last small step,

Pass the image URL of the custom LayoutAttributes to the decorator view, On – (void) applyLayoutAttributes layoutAttributes method: (UICollectionViewLayoutAttributes *).

This method is called when collectionView is configured to decorate the view. LayoutAttributes as an argument, take the imgUrlStr attribute and use it

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
    if ( [layoutAttributes valueForKey: @"imgUrlStr"] && [layoutAttributes isMemberOfClass: NSClassFromString(@"DecorationLayoutAttributes")] ) {
        [self.imageView sd_setImageWithURL_str: [layoutAttributes valueForKey: @"imgUrlStr"]]. }}Copy the code

Second, how to deal with, look at the great god write CHTCollectionViewWaterfallLayout

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSArray<UICollectionViewLayoutAttributes *> * rawArr = [super layoutAttributesForElementsInRect: rect];
    NSMutableArray<UICollectionViewLayoutAttributes *> * array = [[NSMutableArray alloc] initWithArray: rawArr];
    NSInteger numberOfSections = [self.collectionView numberOfSections];
//    if (numberOfSections == 0) {
//        return rawArr;
//    }
UICollectionViewLayoutAttributes * decorationAttrs = [self layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView atIndexPath: [NSIndexPath indexPathForItem: 0 inSection: 1 ]]; // Because of this line, crash // there is no actual interval until the data request returns. IndexPath doesn't have one either.Copy the code

230718+0800 Improved[31532:238435] *** Terminating app due to uncaught Exception ‘NSInternalInconsistencyException’, reason: ‘request for layout attributes for decoration view of kind FrontDecorationReusableView in section 1 when there are only 0 sections in the collection view’

Datasource datasource not set, return first

Judge the situation

 if (numberOfSections == 0) {
        return rawArr;
    }
Copy the code

The third point,

If determines the condition directly and has a random semantics.

A dictionary is a hash table, you know the keys, you just value them. There is also a random semantics.

It’s perfect for this random configuration.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{// Last layer, fixed case, simple point, still useifif( indexPath.section == self.floorDataLists.count ){
        HotSalesCell * hotSaleCollectionViewCell = [collectionView dequeueReusableCellWithReuseIdentifier: kHotSaleCollectionViewCell forIndexPath: indexPath];
        MyProduct * myProduct = self.hotSaleProducts[indexPath.row];
       hotSaleCollectionViewCell.hotSalesProduct = myProduct;
       returnhotSaleCollectionViewCell; } UICollectionViewCell * cell = nil; FloorDataList * floorDataList = self.floorDataLists[indexPath.section]; NSString * keyStr = floorDataList.floorTypeName; NSNumber * newSectionIndex = self.mapone_sequence [keyStr]; // Switch the configuration to a finite set. // Switch the configuration to a finite set.caseFloorDataModel * FloorDataModel = floorDataList. FloorData [indexpath.item]; switch (newSectionIndex.unsignedIntegerValue){case 1:
       {
           BannerReusableView * bannerReusableView = [collectionView dequeueReusableCellWithReuseIdentifier:  kBannerReusableView forIndexPath: indexPath];
               NSMutableArray * imgLinksArray = [NSMutableArray array];
               for (FloorDataModel * floorDataModel in floorDataList.floorData){
                   [imgLinksArray addObject: floorDataModel.uploadImage];
               }
               [bannerReusableView parseBannerPics: imgLinksArray  andSection: indexPath.section];
           }
           / / Cell A 
           cell = bannerReusableView;
       }
           break;
       case 2:
           {
               cell = // ... Cell B; 
           }
           break;
       case 3:
           {
                cell = // ... Cell C; 
           }
           break;
       case 4:
       {
            cell = // ... Cell D; 
       }
           break;
       default:
           break;
   }
   return cell;
}


- (NSDictionary *)mapOne_sequence{
   if(! _mapOne_sequence) { _mapOne_sequence = @{kFloorTypeNameFiveIcon: @(2), kFloorTypeNameBanner: @(1), kTenGoods: @(3), kAcrossColumn: @(4) }; }return _mapOne_sequence;
}


Copy the code

First, find the key floor configuration information as the key

Using dictionaries, turn unordered configurations into finite set cases

Use the switch… Case, for each case, it’s just a matter of dealing with it

(I haven’t come up with a better idea yet)

See more of the Demo code:

Dev.tencent.com/u/dengjiang…

Check out a blog post written by casA Bigwigs


References:

Stackoverflow.com/questions/1…