iOS瀑布流实现(Swift)
這段時間突然想到一個很久之前用到的知識-瀑布流,本來想用一個簡單的方法,發(fā)現(xiàn)自己走入了歧途,最終只能狠下心來重寫UICollectionViewFlowLayout.下面我將用兩種方法實現(xiàn)瀑布流,以及會介紹第一種實現(xiàn)的bug.
<1>第一種
效果圖如下所示:
這種實現(xiàn)方法的思路: ?
? 1)首先調(diào)用隨機函數(shù),產(chǎn)生隨機高度,并把它保存到數(shù)組中
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {CGFloat cellW = 100;CGFloat cellH = 100 + (arc4random() % 80);[self.heightArrayM addObject:@(cellH)];return CGSizeMake(cellW, cellH);}2)在設置cell的frame的地方,通過取余,取整確定cell的高度,并設定cell的frame
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];//當前處于多少行NSInteger num1 = indexPath.row / count;//當前處于多少列int num2 = indexPath.row % count;CGFloat cellX = num2 * 100 + (num2 + 1) * margin;CGFloat cellY = 0;for (int i = 0; i < num1; i++) {NSInteger position = num2 + i * 3;cellY += [self.heightArrayM[position] floatValue] + margin;}CGFloat cellW = 100;CGFloat cellH = cellHeight;cell.frame = CGRectMake(cellX, cellY, cellW, cellH); // cell.backgroundColor = [UIColor redColor];cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];// NSLog(@"%@", NSStringFromCGRect(cell.frame)); return cell; }弊端 : 其實這種方法的弊端,相信從上面的動態(tài)圖中可以看出來,當往上面滑的時候,由于cell的循環(huán)機制,下面的cell的會消失,但是由于高度不一致,同時撤銷的是最后一行的cell,所以下面的cell在屏幕上就會消失.
下面附上第一種方法的源代碼:
#import "ViewController.h"#define margin 10 #define count 3 #define cellHeight [self.heightArrayM[indexPath.row] floatValue] static NSString * const ID = @"cell"; @interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; @property (nonatomic, strong) NSMutableArray *heightArrayM;@end@implementation ViewController- (NSMutableArray *)heightArrayM {if (_heightArrayM == nil) {_heightArrayM = [NSMutableArray array];}return _heightArrayM; }- (void)viewDidLoad {[super viewDidLoad];[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:ID];self.collectionView.dataSource = self;self.collectionView.delegate = self;//設置collectionView [self setupCollectionView]; }//設置collectionView的布局 - (UICollectionViewFlowLayout *)setupCollectionLayout {UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];flowLayout.minimumInteritemSpacing = margin;flowLayout.minimumLineSpacing = margin;flowLayout.sectionInset = UIEdgeInsetsMake(margin, margin, margin, margin);return flowLayout; }//設置collectionView - (void)setupCollectionView {self.collectionView.collectionViewLayout =[self setupCollectionLayout];}#pragma mark - UICollectionViewDataSouce - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return 60; }- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];//當前處于多少行NSInteger num1 = indexPath.row / count;//當前處于多少列int num2 = indexPath.row % count;CGFloat cellX = num2 * 100 + (num2 + 1) * margin;CGFloat cellY = 0;for (int i = 0; i < num1; i++) {NSInteger position = num2 + i * 3;cellY += [self.heightArrayM[position] floatValue] + margin;}CGFloat cellW = 100;CGFloat cellH = cellHeight;cell.frame = CGRectMake(cellX, cellY, cellW, cellH); // cell.backgroundColor = [UIColor redColor];cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];// NSLog(@"%@", NSStringFromCGRect(cell.frame)); return cell; }- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {CGFloat cellW = 100;CGFloat cellH = 100 + (arc4random() % 80);[self.heightArrayM addObject:@(cellH)];return CGSizeMake(cellW, cellH);} @end<2>下面介紹第二種(Swift實現(xiàn))
效果圖如下所示:
這種實現(xiàn)方法就是比較成熟的了,我把它封裝成一個類.其實主要是實現(xiàn)三個函數(shù)
? 1)重寫父類的prepare方法,準備所有cell的樣式
extension WaterfallLayout {// prepare準備所有Cell的布局樣式override func prepare() {super.prepare()// 0.獲取item的個數(shù)let itemCount = collectionView!.numberOfItems(inSection: 0)// 1.獲取列數(shù)let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2// 2.計算Item的寬度let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)// 3.計算所有的item的屬性for i in startIndex..<itemCount {// 1.設置每一個Item位置相關的屬性let indexPath = IndexPath(item: i, section: 0)// 2.根據(jù)位置創(chuàng)建Attributes屬性let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)// 3.隨機一個高度guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {fatalError("請設置數(shù)據(jù)源,并且實現(xiàn)對應的數(shù)據(jù)源方法")}// 4.取出最小列的位置var minH = colHeights.min()!let index = colHeights.index(of: minH)!minH = minH + height + minimumLineSpacingcolHeights[index] = minH// 5.設置item的屬性attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)attrsArray.append(attrs)}// 4.記錄最大值maxH = colHeights.max()!// 5.給startIndex重新復制startIndex = itemCount} }2)返回設置cell樣式的數(shù)組
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {return attrsArray}3)返回當前的contentSize
override var collectionViewContentSize: CGSize {return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)}總結:
在下面我封裝的這個類中,只需要遵守我的數(shù)據(jù)代理源協(xié)議并且實現(xiàn)我的協(xié)議中的兩個方法,傳給我對應得高度(我這里是傳的隨機的),可選的方法,若是不實現(xiàn),會有一個默認值,就可以實現(xiàn)該功能.協(xié)議如下:
@objc protocol WaterfallLayoutDataSource : class {func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int }?
完成代碼如下所示:
ViewController.swift中的代碼:
封裝自定義布局中的WaterfallLayout.swift代碼如下:
import UIKit@objc protocol WaterfallLayoutDataSource : class {func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int }class WaterfallLayout: UICollectionViewFlowLayout {// MARK: 對外提供屬性weak var dataSource : WaterfallLayoutDataSource?// MARK: 私有屬性fileprivate lazy var attrsArray : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()fileprivate var totalHeight : CGFloat = 0fileprivate lazy var colHeights : [CGFloat] = {let cols = self.dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2var colHeights = Array(repeating: self.sectionInset.top, count: cols)return colHeights}()fileprivate var maxH : CGFloat = 0fileprivate var startIndex = 0 }extension WaterfallLayout {// prepare準備所有Cell的布局樣式override func prepare() {super.prepare()// 0.獲取item的個數(shù)let itemCount = collectionView!.numberOfItems(inSection: 0)// 1.獲取列數(shù)let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2// 2.計算Item的寬度let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)// 3.計算所有的item的屬性for i in startIndex..<itemCount {// 1.設置每一個Item位置相關的屬性let indexPath = IndexPath(item: i, section: 0)// 2.根據(jù)位置創(chuàng)建Attributes屬性let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)// 3.隨機一個高度guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {fatalError("請設置數(shù)據(jù)源,并且實現(xiàn)對應的數(shù)據(jù)源方法")}// 4.取出最小列的位置var minH = colHeights.min()!let index = colHeights.index(of: minH)!minH = minH + height + minimumLineSpacingcolHeights[index] = minH// 5.設置item的屬性attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)attrsArray.append(attrs)}// 4.記錄最大值maxH = colHeights.max()!// 5.給startIndex重新復制startIndex = itemCount} }extension WaterfallLayout {override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {return attrsArray}override var collectionViewContentSize: CGSize {return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)} }?
轉載于:https://www.cnblogs.com/muzichenyu/p/6108040.html
總結
以上是生活随笔為你收集整理的iOS瀑布流实现(Swift)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4、集合
- 下一篇: Eclipse用户使用IntelliJ