天天看點

CollectionView和layout的使用,瀑布流

1 .CollectionView

CollectionView是tableView的更新版,可以了解為多列的tableView,用法也tableView也基本相同,但也有不同的

CollectionViewCell和TableViewCell一樣,有cell,隻是這裡叫Item,我們可以規定Item的位置,但是這裡item預設不具有cell的三大屬性(imageView,textLabel,detailTextLabel),是以在設定cell的這些設定時,我們可以采用自定義MyCollectionViewCell,在這設定這些屬性即可

2 .layout  --- 也叫布局

UICollectionViewLayout是一個基類,是所有的布局檔案的父類,但是裡面并沒有元素位置的定義

UICollectionViewFlowLayout是上面的一個子類,他實作了元素位置的定義,實作了流水式布局

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayoutalloc]init];

    // 設定每一個元素的大小

    layout.itemSize =CGSizeMake(100,100);

    //  設定内邊距大小

    layout.sectionInset =UIEdgeInsetsMake(20,20, 40, 20);

    //  設定滾動方向

//    layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

    //  設定最小行間距

    layout.minimumLineSpacing =50;

    //  設定最小列間距

    layout.minimumInteritemSpacing =10;

   //  行間距的設定一定會實作,列間距的設定不一定會實作,因為存在剩餘空間的平均配置設定問題

   //  行間距因滾動方向的不同作用的效果方向也不同.

    self.collection = [[UICollectionViewalloc] initWithFrame:[UIScreenmainScreen].boundscollectionViewLayout:layout];

    [selfaddSubview:self.collection];

我們還可以在RootViewController對connection設定代理為目前controller,讓代理調用方法,傳回item的size 例如:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    if (indexPath.row %2) {

        return CGSizeMake(120, 120);

    } else {

        return CGSizeMake(80, 80);

    }

}

3 .瀑布流

CollectionView和layout的使用,瀑布流

如果我們想要實作瀑布流的效果,需要對每一個item的size根據對應的image進行比例縮放,如果用系統自帶的layout難以實作這個要求,是以我們隻需自定義layout,其他不改變即可

基于UICollectionViewLayout建立WaterFlowLayout

WaterFlowLayout.h中

#import <UIKit/UIKit.h>

@protocol WaterFlowLayoutDelegate <NSObject>

// 通過indexPath位置的圖檔,根據圖檔的寬高的比例,以及item的width計算出indexPath的item的高度,最終通過在controller中指定代理,實作該方法

- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath;

@end

@interface WaterFlowLayout : UICollectionViewLayout

//  給自定義layout建立Layout所具備的以下幾個屬性

//  Item的大小,最主要的是根據這個擷取到列寬

@property (nonatomic,assign) CGSize itemSize;

// 内邊距的設定

@property (nonatomic,assign) UIEdgeInsets sectionInsets;

//  Item的間距

@property (nonatomic,assign) CGFloat spacing;

// 一共有幾列

@property (nonatomic,assign) NSInteger numberOfColumns;

// 代理,用于綁定資料,計算高度的

@property (nonatomic,assign) id <WaterFlowLayoutDelegate>delegate;

@end

WaterFlowLayout.m中

@interfaceWaterFlowLayout ()

//  儲存一共有幾個Item

@property (nonatomic,assign) NSInteger numberOfItems;

// 儲存計算好的每一個item的位置資訊

@property (nonatomic,strong) NSMutableArray *itemAttributes;

// 儲存每一列的高度

@property (nonatomic,strong) NSMutableArray *columnsHeights;

// 找到目前最長列的标号

- (NSInteger)indexForHeightestColumn;

// 找到目前最短列的标号

- (NSInteger)indexForShortestColumn;

@end

@implementation WaterFlowLayout

// 懶加載屬性值

- (NSMutableArray *)columnsHeights {

    if (_columnsHeights ==nil) {

        _columnsHeights = [NSMutableArrayarray];

    }

    return_columnsHeights;

}

- (NSMutableArray *)itemAttributes {

    if (_itemAttributes ==nil) {

        _itemAttributes = [NSMutableArrayarray];

    }

    return_itemAttributes;

}

- (NSInteger)indexForHeightestColumn {

    NSInteger index =0;

    //  記錄最長的長度

    CGFloat length =0;

    for (int i =0; i < self.numberOfColumns; i++) {

        // 将數組裡的對象轉換成float類型的值

        CGFloat currentValue = [self.columnsHeights[i]floatValue];

        if (currentValue > length) {

            length = currentValue;

            index = i;

        }

    }

    return index;

}

- (NSInteger)indexForShortestColumn {

    NSInteger index =0;

    CGFloat length =MAXFLOAT;

    for (int i =0; i < self.numberOfColumns; i++) {

        CGFloat currentValue = [self.columnsHeights[i]floatValue];

        if (currentValue < length) {

            length = currentValue;

            index = i;

        }

    }

    return index;

}

// 準備布局,在這裡計算每一個Item的frame

- (void)prepareLayout {

    [superprepareLayout];

   //  拿到一共有多少個元素

    self.numberOfItems = [self.collectionViewnumberOfItemsInSection:0];

    //  給每一列添加一個top的高度

    for (int i =0; i < self.numberOfColumns; i++) {

        self.columnsHeights[i] =@(self.sectionInsets.top); // NSNumber

    }

   //  挨個去為每一個元素建立屬性資訊,并存放到數組中

    for (int i =0; i < self.numberOfItems; i++) {

        //  1 确定元素高度最短列

        NSInteger shortestIndex = [selfindexForShortestColumn];

        //  2 拿到最短列的高度備用

        CGFloat height = [self.columnsHeights[shortestIndex]floatValue];

        //  3 計算x的值  目标x = 内邊距的左邊距 + (寬 + Item間距) *列數

        CGFloat detalX =self.sectionInsets.left + (self.itemSize.width + self.spacing) * shortestIndex;

        //  4 計算y的值

        CGFloat detalY = height +self.spacing;

        //  5 建立屬性

        NSIndexPath *indexPath = [NSIndexPathindexPathForItem:i inSection:0];

        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];

        //  6 調用代理計算高度

        CGFloat itemHeight =0;

        if (_delegate && [_delegaterespondsToSelector:@selector(heightForItemIndexPath:)]) {

            //  計算高度

            itemHeight = [_delegateheightForItemIndexPath:indexPath];

        }

        //  7 生成frame

        attributes.frame =CGRectMake(detalX, detalY, self.itemSize.width, itemHeight);

        //  8 将這個資訊對象放到數組裡面

        [self.itemAttributesaddObject:attributes];

        //  9 更新這一列的高度

        self.columnsHeights[shortestIndex] =@(detalY + itemHeight);

    }

}

//  傳回contentView的大小

- (CGSize)collectionViewContentSize{

    //  求最高列的高度

    NSInteger longestIndex = [selfindexForHeightestColumn];

    CGFloat height = [self.columnsHeights[longestIndex]floatValue];

    //  拿到contentView的原始大小

    CGSize size =self.collectionView.frame.size;

    size.height = height +self.sectionInsets.bottom;

    return size;

}

//  傳回每一個Item的Attributes(屬性資訊)

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {

    returnself.itemAttributes;

}

@end

建立完自定義Layout,就可以利用此WaterFlowLayout建立瀑布流的專屬Layout

RootView.m中

//  使用瀑布流的專屬Layout

    self.layout = [[WaterFlowLayoutalloc] init];

    //  設定屬性

    CGFloat w = ([UIScreenmainScreen].bounds.size.width -40) / 3;

    self.layout.itemSize =CGSizeMake(w, 10);

    //  内邊距

    self.layout.sectionInsets =UIEdgeInsetsMake(10,10, 10, 10);

    // item 間距

    self.layout.spacing =10;

    //  一共有幾列

    self.layout.numberOfColumns =3;

    self.collection = [[UICollectionViewalloc] initWithFrame:[UIScreenmainScreen].boundscollectionViewLayout:self.layout];

    [selfaddSubview:self.collection];

RootViewController.m 中

接受WaterFlowLayout中的代理,并指定代理為目前controller

實作代理中的方法

- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath {

    //  拿到model

    Model *m = self.data[indexPath.item];

    //  擷取圖檔寬度

    CGFloat w = ([UIScreenmainScreen].bounds.size.width -40) / 3;

    CGFloat h = (w * m.height) / m.width;

    return h;

}

model.h中

#import <Foundation/Foundation.h>

@interface Model : NSObject

@property (nonatomic,copy) NSString *thumbURL;

@property (nonatomic,assign) NSInteger width;

@property (nonatomic,assign) NSInteger height;

@end

model.m中  

如果解析時用到添加字典到model

不要忘記 !!!  

@implementation Model

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {

}

@end