Implement IOS waterfall stream UICollectionView and tableview implement waterfall stream
IOS waterfall stream UICollectionView implementation
Before implementing the waterfall stream, let's take a look at the prototype of the waterfall stream (the prototype of this method is UICollectionView)
Notes for UICollectionView
- Unlike tableView, the content of ContentView must be added by ourselves.
- Compared with tableview, FlowLayout is required for initialization and most operations are performed on it.
- UIcollectionView is highly practical. Although it is sometimes not the best solution, it can implement various effects flexibly.
Figure (1)
The simulator shows a lot of squares, but it is worth noting that they have rules.
Although it looks neat but not beautiful.
What we are talking about is to make waterfall flow untidy, but regular (here I am talking about regular)
Question
As mentioned above, most operations of UIcollectionView are performed on FlowLayout, including layout deployment.
What we need to achieve in order to realize the waterfall stream is to change its deployment pattern.
Determine the implementation idea before writing the code.
- What do you need ???
- First, determine the display style of the waterfall stream.
- Then the overall design is carried out based on the determined style.
- Finally, complete the Code through detailed processing
- What kind of style do we need ???
- What we need is to change the layout in the above image to a different effect.
- Just like a name, just like a waterfall
- How should we design it as a whole ???
- The overall design is the same as the above image. Each module is a cell.
- Make sure that the cell y value in the top row is the same (beautiful)
- Make sure that a column is not particularly long and has a very short effect.
- What are the preliminary details ???
- Because the height of each cell is different, we need to consider the order of placement.
- Streamline code (this is important for every project)
Effect
Code
The following is the implementation code (it is very easy not to provide a demo)
I will give a brief introduction in the annotations.
---
/// ViewController. m // CX-Waterfall stream UIcollectionView implementation /// Created by ma c on 16/4/8. // Copyright©2016 bjsxt. all rights reserved. // # import "ViewController. h "# import" CXCollectionViewCell. h "# import" CXCollectionViewLayout. h "static NSString * identifier = @" cellID "; @ interface ViewController () <UICollectionViewDataSource> // The UICollectionView to be displayed @ property (nonatomic, strong) UICollectionView * collectionView; @ end @ implementation ViewController # pragma mark-<lazy loading>-(UICollectionView *) collectionView {If (! _ CollectionView) {// initialize our custom flowLayout CXCollectionViewLayout * flowLayout = [[CXCollectionViewLayout alloc] init]; // initialize collectionView _ collectionView = [[UICollectionView alloc] initWithFrame: self. view. bounds collectionViewLayout: flowLayout]; // sets the data source (the root of collectionView) _ collectionView. dataSource = self; // register our custom cell [_ collectionView registerNib: [UINib nibWithNibName: NSStringFromClass ([CXCollectionViewCell class]) bundle: nil] identifier: identifier];} return _ collectionView; }# pragma mark-<life>-(void) viewDidLoad {[super viewDidLoad]; // In self. add --- [self. view addSubview: self. collectionView] ;}# pragma mark-<UICollectionViewDataSource> // The number of items returned here returns 100-(NSInteger) collectionView :( UICollectionView *) collectionView numberOfItemsInSection :( NSInteger) section {return 100;} // here the returned cell is returned. Here we can perform some simple operations-(UICollectionViewCell *) collectionView :( UICollectionView *) collectionView cellForItemAtIndexPath :( NSIndexPath *) indexPath {CXCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier: identifier forIndexPath: indexPath]; // The Label cell we added for the implementation details of the waterfall stream. label. text = [NSString stringWithFormat: @ "% zd", indexPath. item]; // cell background color cell. backgroundColor = [UIColor orangeColor]; return cell;} @ end
---
/// CXCollectionViewLayout. m // CX-Waterfall stream UIcollectionView implementation /// Created by ma c on 16/4/8. // Copyright©2016 bjsxt. all rights reserved. // # import "CXCollectionViewLayout. h "// The number of columns in the waterfall stream static NSInteger CXcolumnCount = 3; // The internal margin of the waterfall stream static UIEdgeInsets CXdefaultEdgeInsets = {20, 15, 10, 15 }; // cell column spacing static NSInteger CXcolumnMagin = 10; // cell row spacing static NSInteger CXrowMagin = 10; @ interface CXCollectionViewLayout () // store all cell Layout attributes @ property (nonatomic, strong) NSMutableArray * CXattrsArray; // scale the height of all columns @ property (no Natomic, strong) NSMutableArray * CXcolumnHeights; @ end @ implementation CXCollectionViewLayout # pragma mark-<lazy loading>-(NSMutableArray *) CXattrsArray {if (! _ CXattrsArray) {_ CXattrsArray = [NSMutableArray array];} return _ CXattrsArray;}-(NSMutableArray *) CXcolumnHeights {if (! _ CXcolumnHeights) {_ CXcolumnHeights = [NSMutableArray array];} return _ CXcolumnHeights;} # pragma mark-<prepare layout> // prepare layout (automatically executed before layout)-(void) prepareLayout {// remember to rewrite this method super [super prepareLayout]; // Our data will not remain unchanged in actual operations, therefore, before each layout, we 'd better clear the previously stored attributes // clear the height of all columns // clear the non-attribute of storing all cells [self. CXcolumnHeights removeAllObjects]; [self. CXattrsArray removeAllObjects]; // The height of the cell in the first line is for (NSInteger I = 0; I <CXcolumnCount; I ++) {// The array can only store the object [self. CXcolumnHeights addObject: @ (CXdefaultEdgeInsets. top)];} // create the layout attribute of each cell and add it to the array of the layout attribute of the storage cell. // The total number of cells is as long as a section NSInteger count = [self. collectionView numberOfItemsInSection: 0]; for (NSInteger I = 0; I <count; I ++) {// The created position is indexPath NSIndexPath * indexPath = [NSIndexPath indexPathForItem: I inSection: 0]; // obtain the cell Layout attribute UICollectionViewLayoutAtt corresponding to indexPath Ributes * attributes = [self layoutAttributesForItemAtIndexPath: indexPath]; // Add the obtained layout attribute to the array [self. CXattrsArray addObject: attributes];} // The layout preparation is finished here} // return all cell Layout attributes and overall cell Layout-(NSArray <UICollectionViewLayoutAttributes *> *) layoutattriattributesforelementsinrect :( CGRect) rect {return self. CXattrsArray;} // return the layout attribute of the cell-(UICollectionViewLayoutAttributes *) layoutAttributesForItemAtIndexPath :( NSIndexP Ath *) indexPath {// create layout attribute UICollectionViewLayoutAttributes * CXattributes = [UICollectionViewLayoutAttributes attributes: indexPath]; // obtain the width of collectionView CGFloat collectionViewWidth = self. collectionView. frame. size. width; // The following part is to obtain the cell's frame (layout attribute) CGFloat width; CGFloat height; CGFloat X; CGFloat Y; // obtain the width = (collectionViewWidth-CXdefaultEdgeInsets. left-CX DefaultEdgeInsets. right-(CXcolumnCount-1) * CXcolumnMagin)/CXcolumnCount; // obtain the height. // in actual development, heigh is not really random, but determined based on the data. height is displayed here. Its principle is initially introduced. Therefore, a random number larger than 100 and less than 150 is used. height = 100 + arc4random_uniform (50 ); // obtain X (the implementation focus of the waterfall stream is on the cell's X and Y values) // set the intermediate variable NSInteger tempColumn = 0 for the number of columns; // set an intermediate variable with a small height. Here we will give it the height of the 0th column, which can reduce the number of cycles and improve the efficiency CGFloat minColumnHeight = [self. CXcolumnHeights [0] doubleValue]; for (NSInteger I = 1; I <CXcolumnCount; I ++) {if (minColumnHeight> [self. CXcolumnHeights [I] doubleValue]) {minColumnHeight = [self. CXcolumnHeights [I] doubleValue]; tempColumn = I ;}} X = CXdefaultEdgeInsets. left + (width + CXcolumnMagin) * tempColumn; // obtain Y = minColumnHeight; if (Y! = CXdefaultEdgeInsets. top) {Y + = CXrowMagin;} // sets the frame CXattributes of the cell. frame = CGRectMake (X, Y, width, height); // update the height self of the shortest column. CXcolumnHeights [tempColumn] = @ (CGRectGetMaxY (CXattributes. frame); return CXattributes;} // returns the Content size of collegeView-(CGSize) collectionViewContentSize {// although the returned size is, but here we mainly use height CGFloat maxColumnHeight = [self. CXcolumnHeights [0] doubleValue]; for (NSInteger I = 1; I <CXcolumnCount; I ++) {CGFloat columnHeight = [self. CXcolumnHeights [I] doubleValue]; if (maxColumnHeight <columnHeight) {maxColumnHeight = columnHeight;} return CGSizeMake (0, maxColumnHeight + CXdefaultEdgeInsets. bottom);} @ end
So far, the implementation of waterfall stream has ended.
Here are some points worth noting.
- The cell arrangement in the waterfall flow is based on the height of the current column (for example, if the current third column is the shortest, but under normal circumstances, the cell should be in the first column, at this time, the new cell will be placed in the third column to avoid the height of a column being particularly long or the height of a column being particularly short)
- In actual applications, the cell size is usually processed based on data.
- The content height of UIcollectionView is uncertain, so we need to set the height based on the content.
- When refresh is involved, pay attention to whether the layout attribute of the cell is cleared before the arrival of new data.