1. 程式人生 > >iOS自定義UICollectionViewLayout佈局實現瀑布流

iOS自定義UICollectionViewLayout佈局實現瀑布流

自定義 UICollectionViewLayout 佈局,實現瀑布流;UICollectionView和UICollectionViewCell 另行建立,這只是佈局檔案, 外界控制器只要遵守協議併成為他的代理並實現代理方法heightForItemAtIndex:返回每個cell的高就可以實現溫布流效果,也 可以實現相應的代理方法設定總共有多少列或是行間距列間距與邊框間距。  

ViewLayout.h 檔案:

#import <UIKit/UIKit.h>


@class ViewLayout

;


@protocol ViewLayoutDelegate <NSObject>


// 必須要實現的方法

@required

/** 返回 index位置的item的高 */

- (CGFloat)viewLayout:(ViewLayout *)ViewLayout heightForItemAtIndex:(NSInteger)index width:(CGFloat)width;



// 可選實現的方法

@optional

/** 返回 ViewLayout佈局的最大例數

*/

- (CGFloat)numberOfMaxColumnInViewLayout:(ViewLayout *)viewLayout;


/** 返回 ViewLayout佈局每列的間距 */

- (CGFloat)numberOfColumnMarginInViewLayout:(ViewLayout *)viewLayout;


/** 返回 ViewLayout佈局的行間距 */

- (CGFloat)numberOfRowMarginInViewLayout:(ViewLayout *)viewLayout;


/** 返回 ViewLayout佈局的上左下右邊框間距 */

- (UIEdgeInsets)edgeInsetsOfEdgeInViewLayout:(ViewLayout *)viewLayout;


@end



@interface ViewLayout :UICollectionViewLayout


/** ViewLayout 代理屬性 */

@property (nonatomic,weak) id <ViewLayoutDelegate> delegate;


@end

 

ViewLayout.m 檔案:

#import "ViewLayout.h"


/** 預設最大例數 */

static constNSInteger defaultMaxColumn =3;


/** 預設例間距 */

static constCGFloat defaultdefaultColumnMargin =10;


/** 預設行間距 */

static constCGFloat defaultdefaultRowMargin =10;


/** 預設邊框間距 */

static constUIEdgeInsets defaultEdgeInsets = {10,10 ,10,10};



@interface ViewLayout ()


/** 裝有所有的佈局性的陣列 */

@property (nonatomic,strong) NSMutableArray *attributesArray;


/** 裝有所有有例的高度 */

@property (nonatomic,strong) NSMutableArray *columnHeights;


//方法宣告

- (CGFloat)maxColumn;

- (CGFloat)columnMargin;

- (CGFloat)rowMargin;

- (UIEdgeInsets)edgeInsets;


@end



@implementation ViewLayout


/**  返回最大列數方法實現*/

- (CGFloat)maxColumn{


    if ([self.delegaterespondsToSelector:@selector(numberOfMaxColumnInViewLayout:)]) {

        return [self.delegatenumberOfMaxColumnInViewLayout:self];

    }else{

    

        returndefaultMaxColumn;

    }

}


/** 返回列間距方法實現*/

- (CGFloat)columnMargin{

    if ([self.delegaterespondsToSelector:@selector(numberOfColumnMarginInViewLayout:)]) {

        return [self.delegatenumberOfColumnMarginInViewLayout:self];

    }else{

        

        returndefaultdefaultColumnMargin;

    }

}


/** 行間距方法實現*/

- (CGFloat)rowMargin{

    if ([self.delegaterespondsToSelector:@selector(numberOfRowMarginInViewLayout:)]) {

        return [self.delegatenumberOfRowMarginInViewLayout:self];

    }else{

        

        returndefaultdefaultRowMargin;

    }

}


/** 邊框的間距方法實現*/

- (UIEdgeInsets)edgeInsets{

    if ([self.delegaterespondsToSelector:@selector(edgeInsetsOfEdgeInViewLayout:)]) {

        return [self.delegateedgeInsetsOfEdgeInViewLayout:self];

    }else{

        

        returndefaultEdgeInsets;

    }

}


- (NSMutableArray *)attributesArray{


    if (!_attributesArray) {

        _attributesArray = [NSMutableArrayarray];

    }

    return_attributesArray;

}


- (NSMutableArray *)columnHeights{


    if (!_columnHeights) {

        _columnHeights = [NSMutableArrayarray];

    }

    return_columnHeights;

}


// 準備開始佈局時呼叫方法

- (void)prepareLayout{


    [superprepareLayout];


    [self.columnHeightsremoveAllObjects];

    

    [self.attributesArrayremoveAllObjects];


    

    //先初始給裝有所有例高陣列賦值

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

        self.columnHeights[i] =@(self.edgeInsets.top);

    }


    //獲取第總共的cell數量

    NSInteger count = [self.collectionViewnumberOfItemsInSection:0];

    

    // for迴圈為每為個 cell添加布局屬性

    

    for (int i =0; i < count; ++i) {


        //建立第 i個位置索引

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

        

        //根據索引建立佈局屬性

        UICollectionViewLayoutAttributes *attributes = [selflayoutAttributesForItemAtIndexPath:indexPath];

        

        [self.attributesArrayaddObject:attributes];

    }

}



// 返回所有元素的佈局屬性陣列

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


    returnself.attributesArray;

}



// 返回索引 indexPath位置的cell的布屬性

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{

    

    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];

    

    CGFloat width =self.collectionView.bounds.size.width;


    CGFloat w = (width -self.edgeInsets.left -self.edgeInsets.right - ((self.maxColumn - 1) * self.columnMargin)) /self.maxColumn;

    

    // 執行代理方法拿到代理方法返回的高度

    CGFloat h = [self.delegateviewLayout:selfheightForItemAtIndex:indexPath.itemwidth:w];

   

    // 找出最短的那一例的高和例號

    CGFloat miniHeight = [self.columnHeights[0]doubleValue]; //先初始預設陣列中第0個元素最小值

    NSInteger miniColumn =0;      //初始預設最小例號

    for (int i =1; i < self.maxColumn; ++i) {

        CGFloat height = [self.columnHeights[i]doubleValue];

        if (miniHeight > height) {

            miniHeight = height;

            miniColumn = i;

        }

    }


    CGFloat x =self.edgeInsets.left + miniColumn * (w +self.columnMargin);

    CGFloat y = miniHeight;

    

    if (y !=self.edgeInsets.top) {

        y += self.rowMargin;

    }


    attributes.frame =CGRectMake(x, y, w, h);

    

    //儲存更新當前高到對應例的陣列

    self.columnHeights[miniColumn] =@(CGRectGetMaxY(attributes.frame));


    return attributes;

}


// collectionView的滾動範圍

- (CGSize)collectionViewContentSize{


    //找出最高的所在例

    CGFloat maxHeight =0; //初始最大高為 0

    

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

        

        CGFloat height = [self.columnHeights[i]doubleValue];

        

        if (maxHeight < height) {

            maxHeight = height;

        }

    }


    returnCGSizeMake(0, maxHeight +self.edgeInsets.bottom);

}


@end