Objective:
Before I write this article, I congratulate myself that my GitHub is finally here. This is also my GitHub first code, the following article code can be in the demo clone or download. You are welcome to give your opinion. Do not hesitate to give your star a good written opinion.
Waterfall Flow
First of all, what is waterfall flow?
Waterfall flow, also known as Waterfall flow layout. is a more popular Site page layout, visually displayed as a jagged multi-column layout, as the page scroll bar scroll down, this layout will continue to load the data block and attached to the current trailer. The earliest use of this layout of the site is Pinterest, gradually popular in the country.
Uicollectionview
We know that Uicollectionview is another very, very, very good UI control that Apple has introduced to UITableView, in addition to the UITableView cache pool, the reuse mechanism, you can build your own item (TableView in the cell , the display layout of the item in CollectionView, as long as you pass a layout property to it, he will be able to display the item as you wish.
Uicollectionviewlayout
Here I use the custom class Jrwaterfalllayout to inherit Uicollectionviewlayout to write the waterfall flow layout.
Method |
Description |
– (void) preparelayout |
The method is called when the CollectionView is first laid out and when the layout is invalidated, and it is important to note that the subclass remembers to call Super |
-(nsarray<uicollectionviewlayoutattributes *> *) Layoutattributesforelementsinrect: (CGRect) rect |
Returns an array of layout properties for all elements within a rect range |
– (Uicollectionviewlayoutattributes *) Layoutattributesforitematindexpath: (Nsindexpath *) IndexPath |
Returns the layout properties of an element on a indexpath position |
– (Cgsize) collectionviewcontentsize |
Returns the scroll range of the CollectionView |
Note: Because the Layoutattributesforelementsinrect method calls are very frequent, the array of layout properties should only be computed once and not recalculated every time the method is called
Agent
Provides an interface to modify some of the waterfall flow layout parameters, such as the number of columns displayed, column spacing, leading, margin (uiedgeinsets), if the agent does not implement the method, then use the default parameters: The most important thing is the height of the item!! Because the height of each picture (item) is determined by the aspect ratio of the picture and the Itemwidth. So itemheight must be determined by the agent. Here are a few proxy methods:
Proxy Method |
Description |
@required |
|
– (CGFloat) Waterfalllayout: (Jrwaterfalllayout *) waterfalllayout Heightforitematindex: (nsuinteger) Index width: ( CGFloat) Width |
Returns the height of item under the index position |
@optional |
|
– (Nsuinteger) Columncountofwaterfalllayout: (Jrwaterfalllayout *) waterfalllayout |
Returns the number of columns displayed by the waterfall stream |
– (CGFloat) Rowmarginofwaterfalllayout: (Jrwaterfalllayout *) waterfalllayout |
Return line Spacing |
– (CGFloat) Columnmarginofwaterfalllayout: (Jrwaterfalllayout *) waterfalllayout |
return column Spacing |
– (Uiedgeinsets) Edgeinsetsofwaterfalllayout: (Jrwaterfalllayout *) waterfalllayout |
return Edge Spacing |
Note: Due to the above, Layoutattributesforelementsinrect method calls are very frequent, so proxy methods are bound to be frequently called. But not all proxy methods are @required, so when calling @optional's proxy method, you need the following code to determine whether the proxy responds to the selector each time, in case the agent does not implement the method, and the call causes the program to crash
If ( [self. Delegate
Respondstoselector:@selector(columncountofwaterfalllayout:)] {
_columncount = [self. Delegate columncountofwaterfalllayout:self];
} Else {
_columncount = jrdefaultcolumncount;
}
Every time this judgment is obviously inefficient, we can make a sexual judgment in the Preparelayout method, and then store it with a flags structure, then next time we call the flag to judge. As follows:
struct { //record agent responds to selector
BOOL didrespondcolumncount:1; The 1 here is stored in 1 bytes
BOOL Didrespondcolumnmargin:1;
BOOL Didrespondrowmargin:1;
BOOL didrespondedgeinsets:1;
} _delegateflags;-
(void) setupdelegateflags{
_delegateflags.didrespondcolumncount = [Self.delegate respondstoselector: @selector (columncountofwaterfalllayout:) ];
_delegateflags.didrespondcolumnmargin = [Self.delegate respondstoselector: @selector (columnmarginofwaterfalllayout :)];
_delegateflags.didrespondrowmargin = [Self.delegate respondstoselector: @selector (rowmarginofwaterfalllayout:)];
_delegateflags.didrespondedgeinsets = [Self.delegate respondstoselector: @selector (edgeinsetsofwaterfalllayout:)];
}
So the next time you call the method, it's going to be a little more elegant.
_columncount = _delegateflags.didrespondcolumncount? [Self.delegate columncountofwaterfalllayout:self]: jrdefaultcolumncount;
The most important part of the waterfall flow layout is to find the location of the item. Exactly what the Layoutattributesforitematindexpath method is going to do. Let's start with the idea of finding this item position.
Waterfall Flow Layout Ideas
Here I need to use a total of 2 variable array and a assign property, one to record the height of each column, one to record all itemattributes. Assign is used to record the height of the column with the highest height
/** itemattributes Array */
@Property (nonatomic, strong) nsmutablearray *attrsarray;
/** array of heights per column */
@Property (nonatomic, strong) nsmutablearray *columnheights ;
/** Maximum y value */
Property (nonatomic, assign) cgfloat maxy;
In the Preparelayout method, the above 2 arrays are to be emptied, because the network request of new data to, CollectionView to re-layout if not empty, continue to add things inside, will lead to the layout of the item is all messed up.
The next thing to deal with is how each item in the Layoutattributesforitematindexpath method is laid out. The idea is simple.
Create a Uicollectionviewlayoutattributes object
Calculates the width of the item according to several parameters, such as width and line spacing of the CollectionView
Find the column number of the shortest column
Calculates the X-value, y-value of item according to the column number, asking the agent to get the height of item
Set the Frame property of a Uicollectionviewlayoutattributes object
Returns the Uicollectionviewlayoutattributes object
The main problem is how to calculate x, y, width, height. Look at the picture and talk.
Detailed calculation steps can be seen in the demo.
At last
Theoretically, as long as you have strong enough algorithmic computing power, any display layout can be written out. The collectionviewlayout is not just a waterfall stream!
iOS teaches you to easily create waterfall flow layout