1、思路數組
思路一:比較每一行全部列的cell的高度,從上到下(也就是從第一行開始),從最短的開始計算,(記錄下b的高度和索引,從開始計算,依次類推)佈局
思路二:設置上、下、左、右間距和行間距、列間距及列數。ui
思路三:實現的重要的方法。atom
2、代碼先行。spa
1.自定義layout類。3d
//入口 #import <UIKit/UIKit.h> @protocol STRWaterLayoutDelegate; @interface STRWaterLayout : UICollectionViewLayout @property (nonatomic, weak)id<STRWaterLayoutDelegate>delegate; @end @protocol STRWaterLayoutDelegate <NSObject> @required - (CGFloat)waterLayout:(STRWaterLayout *)waterLayout layoutHeightAtindexPath:(NSIndexPath *)indexpath layoutItemWidth:(CGFloat)width; @optional - (UIEdgeInsets)edgeInsetsWithWaterLayout:(STRWaterLayout *)waterLayout; - (CGFloat)columsNumberWithWaterLayout:(STRWaterLayout *)waterLayout; - (CGFloat)rowsMarginWithWaterLayout:(STRWaterLayout *)waterLayout; - (CGFloat)columsMarginWithWaterLaout:(STRWaterLayout *)waterLayout; @end
//出口 #import "STRWaterLayout.h" static const UIEdgeInsets layoutEdgeInsets = {10, 10, 10, 10}; //上、下、左、右的間距 static const CGFloat columsMar = 10; //列間距 static const CGFloat rowMar = 10; //行間距 static const NSInteger columsNums = 3; //列數 @interface STRWaterLayout() /**每一個cell的高度等信息*/ @property(nonatomic, strong)NSMutableArray *columMinGapArray; /**全部佈局信息的數組*/ @property(nonatomic, strong)NSMutableArray *layoutAttributesArray; /**佈局的最終的高度*/ @property(nonatomic, assign)CGFloat contentHeight; - (UIEdgeInsets)edgeInsets; //上、下、左、右的間距 - (CGFloat)columsNumber; //列數 - (CGFloat)rowMargin; //行間距 - (CGFloat)columsMargin; //列間距 @end @implementation STRWaterLayout #pragma mark --- delegate methods - (UIEdgeInsets)edgeInsets{ if (self.delegate && [self.delegate respondsToSelector:@selector(edgeInsetsWithWaterLayout:)]){ return [self.delegate edgeInsetsWithWaterLayout:self]; }else{ return layoutEdgeInsets; } } - (CGFloat)columsNumber{ if(self.delegate && [self.delegate respondsToSelector:@selector(columsNumberWithWaterLayout:)]){ return [self.delegate columsNumberWithWaterLayout:self]; }else{ return columsNums; } } - (CGFloat)columsMargin{ if(self.delegate && [self.delegate respondsToSelector:@selector(columsMarginWithWaterLaout:)]){ return [self.delegate columsMarginWithWaterLaout:self]; }else{ return columsMar; } } - (CGFloat)rowMargin{ if(self.delegate && [self.delegate respondsToSelector:@selector(rowsMarginWithWaterLayout:)]){ return [self.delegate rowsMarginWithWaterLayout:self]; }else{ return rowMar; } } #pragma mark --- private methods /**在從新佈局時會依次調用這四個方法*/ /**每次從新佈局時都會調用它*/ - (void)prepareLayout{ [super prepareLayout]; self.contentHeight = 0; [self.columMinGapArray removeAllObjects]; [self.layoutAttributesArray removeAllObjects]; for (NSInteger i = 0; i< [self columsNumber]; i++) { //遍歷全部的列數 [self.columMinGapArray addObject: @([self edgeInsets].top)]; } //當前只有一組,因此這麼處理 NSInteger numbers = [self.collectionView numberOfItemsInSection:0]; //處理全部的佈局數據 for (NSInteger y = 0; y <numbers;y++) { //calls layouts methods,調用佈局的方法 UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:y inSection:0]]; //再把算出來的佈局加到佈局數組中。 [self.layoutAttributesArray addObject:layoutAttributes]; } } /**返回橫向滾動或縱向滾動的contentSize*/ - (CGSize)collectionViewContentSize{ return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight+[self edgeInsets].bottom); } /**返回全部UICollectionViewLayoutAttributes的屬性的數組*/ - (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{ return self.layoutAttributesArray; } /**返回每一個indexPath的佈局UICollectionViewLayoutAttributes*/ // 1.首先經過indexPath取出每一個collectionViewLayout的佈局(uicollectionlayoutAttributes). // 2.從存儲每一個cell的高度的數組中取出第一個cell的高度,而後遍歷,存儲最小高度的cell,並記下它的索引,而後在這個索引下的cell增長下一個cell,而後再遍歷看看哪一個cell的高度最低,再把它的高度和索引記下來,增長下下個cell,依次類推。 // 3.由於佈局的寬度和x,y能夠本身設定(也就是能控制),不能控制的是佈局的高度,須要從外部傳進來,由於須要等比例的,因此須要傳當前indexPath的佈局的寬度,而後外面傳的時候用cell的實際寬度*實際高度/當前佈局的寬度。 - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; CGFloat minContentHeight = [self.columMinGapArray[0] doubleValue]; NSInteger minIndex = 0; for (NSInteger i = 1; i < self.columMinGapArray.count; i++) { if (minContentHeight > [self.columMinGapArray[i] doubleValue]) { minContentHeight = [self.columMinGapArray[i] doubleValue]; minIndex = i; } } CGFloat w = (self.collectionView.frame.size.width - [self edgeInsets].left - [self edgeInsets].right - ([self columsNumber]-1) * [self columsMargin])/[self columsNumber]; CGFloat y = (minContentHeight == [self edgeInsets].top ? minContentHeight : minContentHeight + [self rowMargin]); CGFloat x = [self edgeInsets].left + minIndex *(w+[self columsMargin]); CGFloat h = [self.delegate waterLayout:self layoutHeightAtindexPath:indexPath layoutItemWidth:w]; layoutAttributes.frame = CGRectMake(x, y, w, h); self.columMinGapArray[minIndex] = @(CGRectGetMaxY(layoutAttributes.frame)); CGFloat contentHeight = [self.columMinGapArray[minIndex] doubleValue]; if (self.contentHeight < contentHeight) { self.contentHeight = contentHeight; } return layoutAttributes; } #pragma mark --- getters and setters - (NSMutableArray *)columMinGapArray{ if (_columMinGapArray == nil) { _columMinGapArray = [NSMutableArray array]; } return _columMinGapArray; } - (NSMutableArray *)layoutAttributesArray{ if (_layoutAttributesArray == nil) { _layoutAttributesArray = [NSMutableArray array]; } return _layoutAttributesArray; } @end
2.設置collectionView.代理
1).注意事項1,須要設置佈局,上面建立的layout佈局。code
2).注意事項2,須要設置代理和數據源,而後把必須實現的方法實現一下。orm
3).注意事項3,註冊cell有兩種形式,一個是xib,一個是自定義的cell類,自定義或xib的cell類必定是設置collectionViewcell方法中的cell對象。對象
- (UICollectionView *)collectionView{ if (!_collectionView) { STRWaterLayout *layout = [[STRWaterLayout alloc] init]; layout.delegate = self; UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width,self.view.bounds.size.height) collectionViewLayout:layout]; collectionView.delegate = self; collectionView.dataSource = self; collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(headRefresh)]; collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(foorRefresh)]; [collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:customCell]; [self.view addSubview:collectionView]; self.collectionView = collectionView; } return _collectionView; }
3.實現collectionView的數據源方法或代理方法。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.dataArray.count; //存儲的是佈局模型數據,有寬度、高度(最基本的)及其它對象數據。 } // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath: - ( UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:customCell forIndexPath:indexPath]; cell.layoutModel = self.dataArray[indexPath.row]; return cell; }
4.獲取數據。(我這裏是用的plist,這個能夠根據項目來設置,這裏是舉例。)
- (NSMutableArray *)dataArray{ if (!_dataArray) { NSArray *data = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"1" ofType:@"plist"]]; NSMutableArray *dataA = [NSMutableArray array]; for (NSDictionary *dic in data) { STRWaterLayoutModel *layoutModel = [STRWaterLayoutModel getCollectionModel:dic]; [dataA addObject:layoutModel]; } _dataArray = dataA; } return _dataArray; }
5.實現佈局layout的代理,傳入當前cell的高度和間距。
- (CGFloat)waterLayout:(STRWaterLayout *)waterLayout layoutHeightAtindexPath:(NSIndexPath *)indexpath layoutItemWidth:(CGFloat)width{ STRWaterLayoutModel *waterLayoutModel = [self.dataArray objectAtIndex:indexpath.row]; // return width * [waterLayoutModel.h doubleValue] / [waterLayoutModel.w doubleValue]; return [waterLayoutModel.h doubleValue]; } - (CGFloat)columsNumberWithWaterLayout:(STRWaterLayout *)waterLayout{ return 3; } - (CGFloat)columsMarginWithWaterLaout:(STRWaterLayout *)waterLayout{ return 10; } - (CGFloat)rowsMarginWithWaterLayout:(STRWaterLayout *)waterLayout{ return 10; } - (UIEdgeInsets)edgeInsetsWithWaterLayout:(STRWaterLayout *)waterLayout{ return UIEdgeInsetsMake(10,10, 10, 10); }
3、總結。
1.經過上面瀑布流的核心思想。
2.建立collectionView的一些注意事項。
3.給某個類傳數據時,能夠用代理,這纔是正兒八經遵照MVC的思想,後期改動的話,好擴展,若是用屬性的話,後期會很麻煩。
4.代碼地址:待後期上傳。