Implementing a waterfall flow is simple, and implementing a partitioned waterfall flow with a different number of columns in each zone and with a header and a tail is not easy. I don’t want to bother to write my own (– > I admit to being lazy, not willing to move the brain V) began to find a lot of online, are only a waterfall flow, no area head and tail, completely can not meet my needs. No way, the demand of the product is there, can not do it, so I calm down to start writing.

Its methods and properties to imitate UICollectionViewFlowLayout agent, using methods and UICollectionViewFlowLayout similar

Function Description:

  1. Meet UICollectionViewFlowLayout with ordinary linear layout and grid layout

  2. Satisfy single zone and multi-zone waterfall flow layout.

  3. The number of columns per zone can be different when satisfying a multi-zone waterfall flow

  4. Header and footer must be set

  5. The spacing between header and footer is specified

Note: This article does not cover the relevant proxy methods and calculations for decorating views.

First things to understand: collectionView and collocationViewLayout.

CollocationView is responsible for display, collectionviewLayout is responsible for display, including cell size, header and footer size, etc. UICollectionViewFlowLayout inherited from UICollectionViewLayout is apple packaging good layout, you can implement a simple grid and linear layout, When the cell size and spacing can use UICollectionViewFlowLayout, if you want to achieve more complex layout, you need to customize.

Second, to understand UICollectionViewLayoutAttributes attributes of a class, the following is the attribute of each cell, is manifested by UICollectionViewLayoutAttributes attribute.

CGRect frame; // The cell size has x, y values CGPoint center; // The center of the cell CGSize size; // Cell size CATransform3D transform3D; // Cell 3D rotation CGRect bounds NS_AVAILABLE_IOS(7_0); CGAffineTransform transform NS_AVAILABLE_IOS(7_0); // Cell rotation CGFloat alpha; / / alp value NSInteger zIndex; // default is 0 //z axis getter=isHidden) BOOL hidden; // As an optimization,Copy the code

Also, there are several methods to understand UICollectionViewLayout:

  1. PrepareLayout: prepareLayout is specially used to prepare the layout, prepareLayout method in which we can calculate the layout information to be used in advance and store it, to prevent multiple calculations behind the method, improve performance. For example, we can calculate the properties of each cell, the content size of the entire CollectionView, and so on. This method will be called once before layout, only in the call after invalidateLayout, shouldInvalidateLayoutForBoundsChange: returns YES and UICollectionView refresh will call.

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
Copy the code

Returns the attributes of the corresponding indexPath cell

-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
Copy the code

Returns the attributes for the corresponding header and footer

- (CGSize)collectionViewContentSize
Copy the code

; The size of the collectionView, it’s not the size of the viewable range, it’s the size of the entire collectionView

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
Copy the code

Returns attributes for all cell footers and heads in the rect range

Knowing these points will get you started. The calculation is performed from top to bottom, that is, from the head of the region to the cell of each region to the end of the region

Set up arrays to store the calculated values

// Hold an array of attributes

@property (nonatomic, strong) NSMutableArray *attrsArray; @property (nonatomic, strong) NSMutableArray *columnHeights; // The height of the Content of collectionView @ Property (nonatomic, assign) CGFloat contentHeight; // Record the highest @property (nonatomic, assign) CGFloat lastContentHeight for each region; @Property (nonatomic, assign) CGFloat spacingWithLastSection;Copy the code

The first and most important step is to rewrite the prepareLayout method. Initialization is done in this method. All the calculations are set to zero.

Step 1: Pass

[self.collectionView numberOfSections]
Copy the code

Method to get several sections in the collectionView. Set up a for loop.

Step 2: Pass

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
Copy the code

Gets the properties of each header. Once the calculations are complete, attributes are added to the attrsArray array, as well as changing the contentHeight height based on parameters such as sectionInsets

Step 3: After setting the headers, click

[self.collectionView numberOfItemsInSection:i]
Copy the code

Gets how many cells there are in the corresponding range

This is the hardest part of the process. It can be divided into the following steps:

  1. Calculate the frame of each cell. Calculate the width of each cell based on the number of columns in this area and the width of the screen, as well as the spacing between each cell. Since the height is passed from outside, it does not need to be calculated. You also need to know the x and y values of the cell.

X value: First extract which column in the current range is lowest.

NSInteger tempMinColumn = 0; MinColumnHeight = [self.columnheights [0] doubleValue]; // Default minColumnHeight = [self.columnheights [0] doubleValue]; //Copy the code

Take the height of the smallest column

for (NSInteger i = 0; i < self.columnCount; i ++) {

CGFloat columnH = [self.columnHeights[i] doubleValue];

if (minColumnHeight > columnH) {

minColumnHeight = columnH;

tempMinColumn = i;

} else{}}Copy the code

The tempMinColumn is the smallest column

The x value can be calculated based on the sectionInsets, the left and right spacing of each cell, and the width of the cell

CGFloat cellX = self.sectionInsets.left + tempMinColumn * (cellWeight + self.interitemSpacing);
Copy the code

Y value: The height of the smallest column and the height of the smallest column have been determined above.

Y = cellY = minColumnHeight

Note: // If the cell’s y value is not equal to the highest height of the previous cell, i.e., not the first column of the cell, add the spacing between each cell in the cell

if(cellY ! = self.lastContentHeight) { cellY += self.lineSpacing; }else {}
Copy the code

Now we know the frame of the cell, i.e

attributes.frame = CGRectMake(cellX, cellY, cellWeight, cellHeight);
Copy the code
  1. Update contentHeight (the current height of the contents of the collectionView) and columnHeights (the height of each column in the current area or the y value + height of the last cell in each column)

The corresponding cell value is then calculated and added to attrsArray at the return value of this function.

Step 4: The calculation method is the same as header

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
Copy the code

Calculate the footer frame

How many sectors are there, how many header frames are in each sector, how many cells are in each sector, how many frame frames are in each cell, and how many frame frames are in the footer of each sector. In – (NSArray *) layoutAttributesForElementsInRect: (CGRect) calculating the rect returns the attributes

  • [x] Note that the height of the contents of the collectionView (the height of the highest column in the current area) is changing as each attribute is calculated.

in

- (CGSize)collectionViewContentSize {

return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight);

}
Copy the code

CollectionView ContentSize completes the layout.

In order to support scalability and ease of use, I completely imitate UICollectionViewFlowLayout usage set proxy methods and properties. As for its using method, and UICollectionViewFlowLayout. The proxy methods and properties are as follows.

@property (nonatomic, weak) id delegate; @property (nonatomic,assign) UIEdgeInsets sectionInsets; @property (nonatomic,assign) NSInteger columnCount; @property (nonatomic,assign) CGFloat lineSpacing; // The spacing between the left and right sides of each cell @property (nonatomic,assign) CGFloat interitemSpacing; // Header size @property (nonatomic,assign) CGSize headerReferenceSize; // Header size @property (nonatomic,assign) CGSize headerReferenceSize; // assign the size @property (nonatomic,assign) CGSize footerReferenceSize;Copy the code

If the above parameters are the same for each area, you can set them during layout initialization. For example, the first area is two columns, and the second area is one column. Don’t worry, use proxy.

The proxy method supports partition Settings for these parameters.

@protocol JWCCustomLayoutDelegate

@required

/ / cell

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout heightForRowAtIndexPath:(NSIndexPath *)indexPath itemWidth:(CGFloat)itemWidth ;
Copy the code
@optional

// headersize

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
Copy the code

The size of / / footer

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
Copy the code

// The margins of each section

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
Copy the code

// How many columns per column

- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout columnNumberAtSection:(NSInteger )section;
Copy the code

// How many lines are spaced in each section

- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout lineSpacingForSectionAtIndex:(NSInteger)section;
Copy the code

// The left and right space between each item

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout interitemSpacingForSectionAtIndex:(NSInteger)section;
Copy the code

// The distance between the head and the last tail

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout spacingWithLastSectionForSectionAtIndex:(NSInteger)section; (Note: in collectionViewFolwLayout, it is not possible to set the spacing between the current section head and the last section tail. This method is added to make up for this defect.)Copy the code

The above is just a general calculation steps, the specific implementation code see Demo in this

Reprint please indicate the source https://juejin.cn/post/6844903545624330248 thank you

After reading, if it is useful to you, please click the “like” to encourage you to miss? 🤣 🤣

The renderings are as follows