The implementation of the simple waterfall stream of iOS
Objective
Ultra-simple waterfall flow implementation, here to say the author's ideas, detailed code here.
Implementation ideas
CollectionView can realize the layout of each mid-boom, its essence lies in the uicollectionviewlayout, so we want to customize a layout to inherit the system Uicollectionviewlayout, All work is done in this class.
1. Define the required properties
The idea of a waterfall flow is that from top to bottom, that column is the shortest, the next item is placed in which column, so we need to define a dictionary to record the maximum Y value for each column
Each item has a attributes, so define an array to hold each item's attributes.
We also have to know how many columns and column spacing, line spacing, section to CollectionView margins.
//总列数
@property (nonatomic, assign) NSInteger columnCount;
//列间距
@property (nonatomic, assign) NSInteger columnSpacing;
//行间距
@property (nonatomic, assign) NSInteger rowSpacing;
//section到collectionView的边距
@property (nonatomic, assign) UIEdgeInsets sectionInset;
//保存每一列最大y值的数组
@property (nonatomic, strong) NSMutableDictionary *maxYDic;
//保存每一个item的attributes的数组
@property (nonatomic, strong) NSMutableArray *attributesArray;
2. Overriding System methods
We need to rewrite 4 methods altogether.
1)- (void)prepareLayout
2)- (CGSize)collectionViewContentSize
3)- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
4)- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
-(void) Preparelayout method
Some of the pre-layout work is done here, initializing the dictionary, there are several key-value pairs in several columns, key is the first column, value is the maximum y value of the column, and the initial value is the top padding:
for
(int i = 0; i < self.columnCount; i++) {
self.maxYDic[@(i)] = @(self.sectionInset.top);
}
Create the attributes for each item and deposit the array:
//根据collectionView获取总共有多少个item
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
//为每一个item创建一个attributes并存入数组
for
(int i = 0; i < itemCount; i++) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attributesArray addObject:attributes];
}
-(Cgsize) Collectionviewcontentsize method
The contentsize used to calculate CollectionView.
General waterfall flow can only scroll vertically, cannot scroll horizontally, so contentsize.width = 0, we only need to calculate the contentsize.height can
Find the maximum Y value of the longest column from the dictionary, plus the padding below, which is contentsize.height
- (CGSize)collectionViewContentSize {
//假设第0列是最长的那列
__block NSNumber *maxIndex = @0;
//遍历字典,找出最长的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
//如果maxColumn列的最大y值小于obj,则让maxColumn等于obj所属的列
if
([self.maxYDic[maxIndex] floatValue] < obj.floatValue) {
maxIndex = key;
}
}];
//collectionView的contentSize.height就等于最长列的最大y值+下内边距
return
CGSizeMake(0, [self.maxYDic[maxIndex] floatValue] + self.sectionInset.bottom);
}
-(Uicollectionviewlayoutattributes *) Layoutattributesforitematindexpath: (Nsindexpath *) IndexPath method
This method is used to set the attributes of each item, where we simply set the attributes.frame of each item
First we have to know the size of the CollectionView, and then we calculate the width of each item according to the width of the CollectionView, as well as the number of columns, each spacing.
Item width = (width of CollectionView-padding and column padding)/Number of columns
CGFloat collectionViewWidth = self.collectionView.frame.size.width; //self.sectionInset.left:左边距 self.sectionInset.right:右边距 //(self.columnCount - 1) * columnSpacing:一行中所有的列边距 CGFloat itemWidth = (collectionViewWidth - self.sectionInset.left - self.sectionInset.right - (self.columnCount - 1) * self.columnSpacing) / self.columnCount; |
Next, to calculate the coordinates of the item, to calculate the coordinates, you must know the shortest column, walk through the dictionary, and find out which column (Mincolumn) the shortest column is and its maximum y value.
The Y value of item equals the maximum Y value of the shortest column plus the line spacing, and the X value equals the left margin + (item width + column spacing) * mincolumn
//找出最短的那一列
__block NSNumber *minIndex = @0;
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
if
([self.maxYDic[minIndex] floatValue] > obj.floatValue) {
minIndex = key;
}
}];
//根据最短列的列数计算item的x值
CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;
//item的y值 = 最短列的最大y值 + 行间距
CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;
Next is the height of the item, we should calculate the height according to the original size of the image and the calculated width, and so on, but in the layout class, we can't get the picture, so we can define a block property, or proxy, let the outside to calculate and return to us, We need to pass the width of the item and the Indexpath to the outside world:
@property (nonatomic, Strong) CGFloat (^itemheightblock) (CGFloat itemheight,nsindexpath *indexpath);
Sets the height of item according to the return value:
if
(self.itemHeightBlock) itemHeight = self.itemHeightBlock(itemWidth, indexPath);
Finally, set the attributes frame and update the dictionary:
//设置attributes的frame
attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);
//更新字典中的最短列的最大y值
self.maxYDic[minIndex] = @(CGRectGetMaxY(attributes.frame));
-(Nsarray *) Layoutattributesforelementsinrect: (cgrect) Rect method
The method is used to return the attributes of the item within the RECT range.
return directly to Attributesarray
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return
self.attributesArray;
}
Use
The layout class is finished, and then you can use it directly.
//Create layout object
xrwaterfalllayout *waterfall = [[xrwaterfalllayout Alloc] init];
//Setting related properties
waterfall.columncount = 3;
//Total number of columns
waterfall.columnspacing = 10;
//Column spacing
waterfall.rowspacing = 10;
//Line spacing
Waterfall.sectioninset = uiedgeinsetsmake (10, 10 , 10, 10);
//Padding
[Waterfall setitemheightblock:^cgfloat (Cgfloat itemwidth, nsindexpath *indexpath) {
//Calculate the display height according to the original size of the picture, and the display width, and so on,
xrimage *image = self.images[ Indexpath.item];
return
image.imageh / image.imagew * itemwidth;
}];
Collectionview.collectionviewlayout = waterfall;
...
...
Specific code please download here: https://github.com/codingZero/XRWaterfallLayout, feel good, please offer your star
The implementation of the simple waterfall stream of iOS