Establishment of waterfall stream framework and waterfall stream framework

Source: Internet
Author: User

Establishment of waterfall stream framework and waterfall stream framework

Everyone should be familiar with waterfall stream. Most e-commerce applications now use waterfall stream more or less, which can attract users' eyes and make them difficult to produce visual fatigue, apple added the UICollectionView control to iOS6. This control is an upgraded version of UITableView. With this control, we can easily make waterfall streams, later, we can use our own encapsulation to turn it into a small framework and use it more easily in subsequent development.

I have recently opened a short book to welcome everyone's attention. I will occasionally share my iOS development experience and click here --> Melody_Zhy

If you want to create a waterfall stream, you need to customize CollectionViewFlowLayout, because this class has a method to return the layout attributes of all the child controls in collectionView (the layout attributes include the frame and index of the control ).

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {    NSArray *arr = [super layoutAttributesForElementsInRect:rect];    for (UICollectionViewLayoutAttributes *attri in arr) {        attri.frame = CGRectMake(0, 0, 100, 300);    }    NSLog(@"%@", arr);    return arr;}

This method calculates the layout attributes of all sub-controls in collectionView once, and is cached after calculation. When the cell has been computed, when it appears again, it will not calculate its size repeatedly.

Note: If you want to refresh the data, remember to clear the previous layout attributes. Otherwise, a layout error occurs.
// Clear the data used to hold all layout attributes [self. attrArrM removeAllObjects];

Now we define a variable array attribute to store the frame in the layout attribute calculated by ourselves for a moment, so that the above method returns the custom Layout attribute.

// Variable array used to save all layout attributes @ property (nonatomic, strong) NSMutableArray * attrArrM;

In which method do we calculate the custom Layout attributes?

There is such a method

-(Void) prepareLayout {// call the prepareLayout [super prepareLayout] of the parent class;}
When all the sub-controls in collectionView are about to be displayed, this method will be called to prepare for layout and prepare itemSize... when the layout property changes, this method is also called. After the data is refreshed, this method is called to re-Prepare the layout.

In this method, you can use the numberOfItemInSection method of collectionView in collectionViewFlowLayout to obtain all cells in a group.

// Obtain all cellNSInteger cellCount = [self. collectionView numberOfItemsInSection: 0];

Create a layout attribute through a for loop (number of cycles is the number of cells in a group). In the loop, the frame of each cell needs to be calculated. Therefore, the background must provide the actual image size (if not, the size calculated by yourself may cause image flash)

It usually scales proportionally when displayed.

Cell width calculation cell width = (content width-(columns-1) * minimum spacing)/column content width = collectionView width-left margin of the group-right margin
CGFloat contentWidth = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right;CGFloat cellW = (contentWidth - (self.columnCount - 1) * self.minimumInteritemSpacing) / self.columnCount;
Cell height acquisition

To obtain the height of a cell, you need to use the Controller to obtain the height of the model image (the height must be proportional to itemW or the image will be too large height/width * itemW ;), therefore, we need to make the Controller our proxy in the Custom CollectionViewFlowLayout. the protocol defined in the H file is as follows:

#import <UIKit/UIKit.h>@class ZHYCollectionViewFlowLayout;@protocol ZHYCollectionViewFlowLayoutDelegate <NSObject>@required- (CGFloat)waterFallFlowLayoutWithItemHeight:(ZHYCollectionViewFlowLayout *)flowLayout itemW:(CGFloat)itemW CellIndexPath:(NSIndexPath *)indexPath;@end
And set the proxy attributes as follows:
@property (weak, nonatomic) id<ZHYCollectionViewFlowLayoutDelegate> delegate

When cell height is calculated, the proxy method is called to obtain the cell height.

CGFloat cellH = [self.delegate waterFallFlowLayoutWithItemHeight:self itemW:cellW CellIndexPath:indexPath];
Why add indexPath to the proxy method, because indexPath is used to obtain the calculation of the cellX Model

When calculating cellX, if you use NSInteger col = I % self. columnCount; to obtain the column number

Pay attention to one problem:

In this way, the images are arranged in sequence. If the sizes of the images are uneven, the image size may be extremely short, and the image size may be extremely long, unfortunately, long rows are in the same column and short columns, which may cause poor aesthetics. How can we solve this problem? Can we make it possible for each row to be added, when the next line is added, what about the first image with the shortest height in the previous line? Of course the answer is yes -_-!

At this time, we need to define a variable dictionary attribute to store the height of each column and use the column number as the dictionary key.

// Records the highest height of each column @ property (nonatomic, strong) NSMutableDictionary * colDict;
In the prepareLayout method, a default height is set for the height Dictionary of each column.
For (NSInteger I = 0; I <several columns; I ++) {NSString * str = [NSString stringWithFormat: @ "% ld", I]; self. colDict [str] = @ (self. sectionInset. top );}
Method for obtaining the smallest column number
- (NSString *)minCol {    __block NSString *min = @"0";    [self.colDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {        if ([obj floatValue] < [self.colDict[min] floatValue]) {            min = key;        }    }];    return min;}
Therefore, the column number is
NSInteger col = [[self minCol] integerValue];
CellX is:
CGFloat cellX = self.sectionInset.left + (cellW + self.minimumInteritemSpacing) * col;
CellY computing

Computing cellY

// Use the column number as the dictionary's keyNSString * colStr = [NSString stringWithFormat: @ "% ld", col]; CGFloat cellY = [self. colDict [colStr] floatValue]; // calculates the height of each column self. colDict [colStr] = @ (cellY + cellH + self. minimumLineSpacing );

In this way, the X, Y, W, H of each cell is calculated. Let's set the frame of the layout attribute.

// Set the properties of frameattr. frame = CGRectMake (cellX, cellY, cellW, cellH );
At the same time, we also add a layout attribute to the tail view. The Code is as follows:
// Create the layout attribute of the end view // create a footerview index NSIndexPath * indexPath = [NSIndexPath indexPathForItem: 0 inSection: 0]; // It must be an additional parameter UICollectionViewLayoutAttributes * footerAttr = [UICollectionViewLayoutAttributes attributes: UICollectionElementKindSectionFooter withIndexPath: indexPath]; footerAttr. frame = CGRectMake (0, [self. colDict [self. maxCol] floatValue]-self. minimumLineSpacing, self. collectionView. bounds. size. width, 50); [self. attrArrM addObject: footerAttr];
The highest column is used here, because the tail view should be placed at the bottom of the cell, and the method to obtain the highest column index is:
// Used to retrieve the column number of the highest column-(NSString *) maxCol {_ block NSString * maxCol = @ "0"; [self. colDict enumerateKeysAndObjectsUsingBlock: ^ (id _ Nonnull key, id _ Nonnull obj, BOOL * _ Nonnull stop) {if ([obj floatValue]> [self. colDict [maxCol] floatValue]) {maxCol = key ;}}]; return maxCol ;}
Finally, we use the method described above to return the layout Property
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {    return self.attrArrM;}
Pay attention to the returned actual contentSize when customizing the layout attribute. The Code is as follows:
- (CGSize)collectionViewContentSize {    return CGSizeMake(0, [self.colDict[self.maxCol] floatValue] + self.footerReferenceSize.height - self.minimumLineSpacing);}

This is basically done, but if I want to make the waterfall stream layout a simple framework, we need to implement some initialization methods in a simple way.

In CollectionViewFlowLayout. h, you need to define the attributes of several cells in a row. When the Controller references this class, you can set it yourself.

@property (assign, nonatomic) NSInteger columnCount;

Some initialization methods are provided to enable a row to have three cells by default. The cell spacing and row spacing are 10, the padding is 20 at the top, footerReferenceSize, and headerReferenceSize are 50, 50

- (instancetype)init{    self = [super init];    if (self) {        self.columnCount = 3;        self.minimumInteritemSpacing = 10;        self.minimumLineSpacing = 10;        self.footerReferenceSize = CGSizeMake(50, 50);        self.headerReferenceSize = CGSizeMake(50, 50);        self.sectionInset = UIEdgeInsetsMake(20, 0, 0, 0);    }    return self;}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{    self = [super initWithCoder:aDecoder];    if (self) {        self.columnCount = 3;        self.minimumInteritemSpacing = 10;        self.minimumLineSpacing = 10;        self.footerReferenceSize = CGSizeMake(50, 50);        self.headerReferenceSize = CGSizeMake(50, 50);        self.sectionInset = UIEdgeInsetsMake(20, 0, 0, 0);    }    return self;}

In this way, the simple waterfall stream framework is successfully built ~

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.