其代理方法和屬性模仿UICollectionViewFlowLayout 所寫现横,使用方法和UICollectionViewFlowLayout類似
功能描述:
1 > 滿足UICollectionViewFlowLayout提供的普通的線性布局和網(wǎng)格布局
2 > 滿足單區(qū)和多區(qū)的瀑布流布局蓬推。
3 > 滿足多區(qū)瀑布流時每個區(qū)的列數(shù)可以不同
4 > 滿足設置header和footer
5 > 滿足設置header和footer的間距
注意:本文不涉及到裝飾視圖的相關代理方法以及計算。
首先要明白的事情:collectionView與collocationViewLayout的關系。
collocationView負責展示示血,collectionviewLayout負責提供如何展示,包括cell的大小位置宴卖,header和footer的大小位置等席纽,UICollectionViewFlowLayout 繼承自UICollectionViewLayout是蘋果公司封裝好的layout,可以實現(xiàn)簡單的網(wǎng)格和線性布局,當cell的大小和間距一樣時可以用UICollectionViewFlowLayout验庙,如果要實現(xiàn)比較復雜的布局顶吮,就需要自定義了。
其次粪薛,要了解UICollectionViewLayoutAttributes 類的屬性悴了,以下是每一個cell的屬性,都是通過UICollectionViewLayoutAttributes屬性體現(xiàn)出來的违寿。
CGRect frame; // cell的大小已經(jīng)x湃交,y值
CGPoint center;//cell的中心點
CGSize size;// cell的size
CATransform3D transform3D;// cell的3D旋轉
CGRect bounds NS_AVAILABLE_IOS(7_0);
CGAffineTransform transform NS_AVAILABLE_IOS(7_0); // cell 的旋轉
CGFloat alpha;//alp值
NSInteger zIndex; // default is 0 //z軸
getter=isHidden) BOOL hidden; // As an optimization,
還有,要理解UICollectionViewLayout的幾個方法:
1. prepareLayout :是專門用來準備布局的陨界,在prepareLayout方法里面我們可以事先就計算后面要用到的布局信息并存儲起來巡揍,防止后面方法多次計算,提高性能菌瘪。例如腮敌,我們可以在此方法就計算好每個cell的屬性、整個CollectionView的內容尺寸等等俏扩。此方法在布局之前會調用一次糜工,之后只有在調用invalidateLayout、shouldInvalidateLayoutForBoundsChange:返回YES和UICollectionView刷新的時候才會調用录淡。
2.
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
返回對應的indexPath的cell的attributes
3.
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
返回對應的header和footer的attributes
4.
- (CGSize)collectionViewContentSize
捌木;collectionView的size 這個size不是可視范圍的size是整個collectionView的size
5.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
返回在rect范圍內所有cell footer和head的attribute
了解以上的幾點就可以開始計算了。計算的順序是從上到下嫉戚,即從區(qū)頭到每個區(qū)的cell再到區(qū)尾
設置一些數(shù)組用于存儲計算好的值:如下
//存放attribute的數(shù)組
@property (nonatomic, strong) NSMutableArray *attrsArray;
//存放當前區(qū)中各個列的當前的高度
@property (nonatomic, strong) NSMutableArray *columnHeights;
//collectionView的Content的高度
@property (nonatomic, assign) CGFloat contentHeight;
//記錄每個區(qū)最高的
@property (nonatomic, assign) CGFloat lastContentHeight;
//每個區(qū)的區(qū)頭和上個區(qū)的區(qū)尾的距離
@property (nonatomic, assign) CGFloat spacingWithLastSection;
首先是重寫 prepareLayout方法刨裆,也是最重要的一步。在此方法中完成初始化彬檀。所有的計算都置為零帆啃。
第一步:通過
[self.collectionView numberOfSections]
方法獲取collectionView中一共有幾個區(qū)。設置一個for循環(huán)窍帝。
第二步:通過
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
獲取每個header的屬性努潘。計算完成之后把attributes添加到attrsArray 數(shù)組中 ,同時還有根據(jù)sectionInsets 等參數(shù)改變contentHeight 的高度
第三步: 設置區(qū)頭完成之后坤学,在循環(huán)中根據(jù)
[self.collectionView numberOfItemsInSection:i]
獲取相應的區(qū)有多少個cell
在這一步中計算是最麻煩的疯坤。可以分為如下步驟:
1> 計算每一個cell的frame深浮。 根據(jù)此區(qū)中一共有幾列和屏幕的寬度压怠,以及每個cell的之間的間距,計算出每個cell的寬度飞苇,因為高度是外面?zhèn)鬟^來的菌瘫,所以高度不需要計算 洋闽。那么還需要知道cell的x值和y值。
x值:首先取出當前區(qū)的中哪一列最低突梦。
NSInteger tempMinColumn = 0; //默認第 0 列最小
CGFloat minColumnHeight = [self.columnHeights[0] doubleValue]; // 取出最小的那一列的高度
for (NSInteger i = 0; i < self.columnCount; i ++) {
CGFloat columnH = [self.columnHeights[i] doubleValue];
if (minColumnHeight > columnH) {
minColumnHeight = columnH;
tempMinColumn = i;
} else {}
}
tempMinColumn 就是最小的那一列
x值就可以根據(jù)sectionInsets 诫舅, 每個cell的左右間距,和cell的寬度算出
CGFloat cellX = self.sectionInsets.left + tempMinColumn * (cellWeight + self.interitemSpacing);
y值:上面已經(jīng)求出高度最小的那一列宫患,以及最小的那一列的高度刊懈。
y值就 cellY = minColumnHeight
注意://如果cell的y值不等于上個區(qū)的最高的高度 即不是此區(qū)的第一列 要加上此區(qū)的每個cell的上下間距
if (cellY != self.lastContentHeight) {
cellY += self.lineSpacing;
} else {}
這樣就可以知道了 cell的frame了, 即attributes.frame = CGRectMake(cellX, cellY, cellWeight, cellHeight);
2> 要更新 contentHeight (當前collectionView的內容的高度) 和columnHeights(當區(qū)的每列的高度或者說每列的最后一個cell的y值 + height)
那么這樣相應cell的值就計算完畢 娃闲,在此函數(shù)返回值處添加到attrsArray 中去虚汛。
第四部:同header的計算方式一樣 在
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
計算footer的frame
一共多少個區(qū) ,每個區(qū)的header的frame是多少皇帮,每個區(qū)中有多少個cell 每個cell的frame是多少 卷哩,每個區(qū)的footer的frame是多少,以此循環(huán)計算出所有的attributes属拾,在- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 返回計算的attributes
注意 :在計算每個attributes時 collectionView的內容的高度即contentHeight collectionView上個區(qū)的最高的那一列的高度即lastContentHeight 都在改變将谊。
在
- (CGSize)collectionViewContentSize {
return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight);
}
中返回collectionView ContentSize 完成布局。
為了支持擴展性和易用性渐白,我完全模仿 UICollectionViewFlowLayout 的用法設置代理方法和屬性尊浓。至于其使用方法,和UICollectionViewFlowLayout 一樣的纯衍。代理方法和屬性如下栋齿。
@property (nonatomic, weak) id delegate;
// 區(qū)的sectionInsets
@property (nonatomic,assign) UIEdgeInsets sectionInsets;
//每個區(qū)的列數(shù)
@property (nonatomic,assign) NSInteger columnCount;
// 每個cell的上下間距
@property (nonatomic,assign) CGFloat lineSpacing;
//每個cell的左右間距
@property (nonatomic,assign) CGFloat interitemSpacing;
//header的size
@property (nonatomic,assign) CGSize headerReferenceSize;
// footer的size
@property (nonatomic,assign) CGSize footerReferenceSize;
上述的這些參數(shù) 如果每個區(qū)都一樣,則可以在layout初始化的時候設置襟诸,如過每個區(qū)的參數(shù)設置都不一樣瓦堵,比如第一個區(qū)是兩列,第二個區(qū)是一列歌亲,不用擔心菇用,用代理。
代理方法支持分區(qū)設置這些參數(shù)应结。
@protocol JWCCustomLayoutDelegate
@required
// cell 高
1
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout heightForRowAtIndexPath:(NSIndexPath *)indexPath itemWidth:(CGFloat)itemWidth ;
@optional
// headersize
1
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
// footer 的 size
1
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
// 每個區(qū)的邊距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
// 每個區(qū)多少列
- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout columnNumberAtSection:(NSInteger )section;
// 每個區(qū)多少中行距
- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout lineSpacingForSectionAtIndex:(NSInteger)section;
// 每個 item 之間的左右間距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout interitemSpacingForSectionAtIndex:(NSInteger)section;
// 本區(qū)區(qū)頭和上個區(qū)區(qū)尾的間距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout spacingWithLastSectionForSectionAtIndex:(NSInteger)section; (注意:在collectionViewFolwLayout中是無法設置當前的區(qū)頭和上個區(qū)尾的間距的刨疼,為了彌補這一缺憾泉唁,特此添加這個方法)