UICollectionView自定義流水佈局
重點是重寫系統的layout方法
1.自定義類繼承UICollectionViewFlowLayout
2.UICollectionViewFlowLayout類會自動調用以下方法
-(void)prepareLayout;
將layout的佈局準備工作都在這裏面執行
-(CGSize)collectionViewContentSize;
返回的是collectionView的滑動範圍,如果是動態加載的,就不要使用這個方法,可以直接設置每個Item的size,系統會通過size大小自動計算滑動範圍
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
返回的是collectionView頁面的佈局,注意此處返回的是一個Attributes的數組
部分代碼:
根據數據計算每個cell的大小
<span style="font-size:14px;">CGFloat columnHeight[self.column];
for (int i = 0; i < self.column; i++){
columnHeight[i] = self.sectionInset.top;
}
CGFloat cellW = (self.collectionView.frame.size.width - (self.column - 1) * self.minimumInteritemSpacing) / self.column;
for (int i = 0; i < self.pictures.count; i++){
Model *model = self.pictures[i];
CGFloat ratio = model.h.doubleValue / model.w.doubleValue;
CGFloat cellH = ratio * cellW;
UICollectionViewLayoutAttributes *layout = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGFloat cellX = i % self.column * (self.minimumInteritemSpacing + cellW);
CGFloat cellY = columnHeight[i % self.column];
layout.frame = CGRectMake(cellX, cellY, cellW, cellH );
//將每個cell的高度存起來
columnHeight[i % 3] += cellH + self.minimumLineSpacing;
[self.layouts addObject:layout];
}
//設置itemSize
CGFloat maxH = 0;
for (int i = 0; i < self.column; i++){
if (maxH < columnHeight[i]) {
maxH = columnHeight[i];
}
}
// NSLog(@"%f",maxH);
//計算行數
CGFloat row = 0;
if (self.pictures.count % self.column == 0) {
row = self.pictures.count / self.column;
}else {
row = self.pictures.count / self.column + 1;
}
CGFloat itemH = (maxH - (self.minimumLineSpacing ) * row) / row;
// CGFloat itemH = (maxH - self.sectionInset.top) / row;
//設置ItemSize系統會自動根據size大小計算應該滑動的範圍
self.itemSize = CGSizeMake(cellW, itemH);</span>
計算的時候要注意行間距和第一行是否留有空隙來計算cell的高度,位置,否則會出現滑動到底的時候cell的底邊和屏幕底邊不會很好貼合
3.添加頭部或底部的視圖
collectionView和tableView一樣,都可以添加頭部和底部視圖,兩者原理一樣,在這裏添加一個底部的
首先實現方法:
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
頭部與底部視圖都是通過這個方法實現,可以用kind的值來區分添加的是哪個視圖
頭部:UICollectionElementKindSectionHeader
底部:UICollectionElementKindSectionFooter
然後在自定義的layout類中計算佈局
<span style="font-size:14px;">//添加footer
UICollectionViewLayoutAttributes *footerAttr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
CGFloat footerH = 50;
footerAttr.frame = CGRectMake(0, maxH, self.collectionView.frame.size.width, footerH);
[self.layouts addObject:footerAttr];</span>
4.在動態加載cell的時候,可能會遇到類似這種錯誤
<span style="font-size:14px;">*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}) changed from <UICollectionViewLayoutAttributes: 0x7fba41449b90> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionFooter); frame = (0 2982.19; 375 50); to <UICollectionViewLayoutAttributes: 0x7fba4164b760> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionFooter); frame = (0 5962.61; 375 50); without invalidating the layout'</span>
這個就是因爲再一次加載layout時,layout的數據不對了,我們的數組裏面一共存放了兩次的佈局,系統不能很好的匹配這些屬性了
這個問題有兩種解決辦法:
(1)在每次計算屬性時都創建一個局部的數組,將每次的數據放在這裏,最後再將其賦給全局的佈局屬性數組即可
(2)創建兩個全局屬性的數組,每次都將其第一個數組,最後將第一個數組裏面的數據放入第二個數組裏,再將第一個數組數據清空(道理與第一個相同,不過賦值時記得用深拷貝,否則清空會兩個數組都爲空)
self.returnSize =self.layouts.mutableCopy;
[self.returnSizesetArray:self.layouts.copy];
[self.layoutsremoveAllObjects];
注:此方法都是在storyboard上做的
附項目源碼:http://download.csdn.net/detail/aa603020460/9387678