iOS之CollectionView瀑布流布局

有一個多月沒有更新博客了,不是不想,而是不知道該寫什么。今天遇到一個說關于瀑布流布局的問題,以前也沒遇到過,就寫寫這個吧,順便用用新的Xcode 8(開發這個行業面很大,做的項目少,很多問題沒有遇到過,但不見得解決不了,日積月累就好了)。

CollectionView是iOS 6引入的控件,今天的重點并不是介紹它,而是使用它進行布局。效果圖如下:


兩列效果圖.png
三列效果圖.png

看了效果圖,再來說說具體的實現步驟:

1.添加CollectionView控件

[self.view addSubview:self.myCollectionView];

2.加載數據(在[self addData]方法中進行數據的處理)

self.myCVLayout.dataArr = [self addData];

3.對CollectionView進行配置(懶加載)[1]

_myCollectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:self.myCVLayout];
    
_myCollectionView.backgroundColor = [UIColor orangeColor];

[_myCollectionView registerNib:[UINib nibWithNibName:@"MyCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:CVCell];
        
_myCollectionView.dataSource = self;

4.對約束layout進行配置(懶加載)

_myCVLayout = [[CollectionLayout alloc]initOptionWithColumnNum:2 rowSpacing:10.0f columnSpacing:10.0f sectionInset:UIEdgeInsetsMake(20, 10, 10, 10)];

5.配置CollectionView的dataSource方法

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.myCVLayout.dataArr.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CVCell forIndexPath:indexPath];
    
    cell.image.image = [UIImage imageNamed:self.myCVLayout.dataArr[indexPath.row]];

    return cell;
}

6.創建一個UICollectionViewLayout的類,并結合第3步CollectionView的配置進行配置

    if (self = [super init]) {
        _columnNum = columnNum;
        _rowSpacing = rowSpacing;
        _columnSpacing = columnSpacing;
        _sectionInset = sectionInset;
        _everyColumnHDict = [NSMutableDictionary dictionary];
        _attributeArr = [NSMutableArray array];
   }

7.計算圖片顯示的高度

    NSString * imageName = _dataArr[i];
    UIImage * image = [UIImage imageNamed:imageName];
    CGFloat imageH = image.size.height / image.size.width * width;
    return imageH;

8.重寫prepareLayout方法[2]

    for (int i = 0; i < _columnNum; i++) {
        [_everyColumnHDict setObject:@(_sectionInset.top) forKey:[NSString stringWithFormat:@"%d",i]];
    }
    
    for (int i = 0; i < _dataArr.count; i++) {
        [_attributeArr addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
    }

9.重寫layoutAttributesForItemAtIndexPath方法

    //創建一個約束
    UICollectionViewLayoutAttributes * attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    //計算寬度(等寬)
    CGFloat itemW = (self.collectionView.bounds.size.width - _sectionInset.left - _sectionInset.right - (_columnNum - 1) * _rowSpacing) / _columnNum;
    //根據寬度算高度
    CGFloat itemH = [self calculateImageHeightWithCount:indexPath.row withWidth:itemW];
    //確定了寬度和高度
    CGRect frame = CGRectMake(0, 0, itemW, itemH);
    //x代表第幾列,y是origin的y
    NSInteger x = 0;
    CGFloat y = 0.0f;
    //遍歷字典即查看每一列的高度,確定最短的一列(x為第幾列,y是高度)
    for (id temKey in _everyColumnHDict) {
        CGFloat temHeight = [_everyColumnHDict[temKey] floatValue];
        if (y == 0) {
            y = temHeight;
            x = [temKey integerValue];
            continue;
        }
        
        if(y > temHeight ) {
            y = temHeight;
            x = [temKey integerValue];
        }
    }
    //設置frame的origin
    frame.origin = CGPointMake(_sectionInset.left + x * (itemW + _rowSpacing), y);
    //將這一列最新的高度保存下來,字典會自動覆蓋原來的數據
    NSString * key = [NSString stringWithFormat:@"%ld",x];
    NSNumber * height = @(_columnSpacing + y + itemH);
    [_everyColumnHDict setObject:height forKey:key];
    //設置并返回約束
    attribute.frame = frame;
    return attribute;

10.重寫collectionViewContentSize方法(設置可滾動的范圍)

    CGFloat height = 0.0f;
    for (id key in _everyColumnHDict) {
        CGFloat temHeight = [_everyColumnHDict[key] floatValue];
        height = height > temHeight ? height : temHeight;
    }
    //遍歷字典,找出最高的那一列,返回高度
    return CGSizeMake(self.collectionView.frame.size.width, height + _sectionInset.bottom);

11.重寫layoutAttributesForElementsInRect方法

return _attributeArr;

注釋寫的非常清楚了,列數、邊距之類的自己改下第4步的參數即可。有不明白的也可以看Demo,歡迎大家Star.

版權聲明:本文為 Crazy Steven 原創出品,歡迎轉載,轉載時請注明出處!


  1. 需要注意第三句代碼,UITableView可以自己創建新的cell,不一定非要注冊,但CollectionView必須要注冊才可使用。 ?

  2. _everyColumnHDict字典的key值代表第幾列,value值代表這一列的高度,這里只是設置了初始值,無論第幾列,第一行的高度都是_sectionInset.top;_attributeArr數組中保存的是每一個item的布局約束 ?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容