自訂UICollectinviewFlowLayout,即實現瀑布流,ios自訂flowlayout
,通過實現不規則的網格分布,來顯示出不同的效果。因為集合視圖必須要指定布局還可以顯示,所以自訂布局就可以實現瀑布流的效果。
//建立布局對象 WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init]; flowLayout.delegate = self; flowLayout.numberOfColumn = 3; //建立集合視圖 UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];
因為系統內建的布局有四個方法,分別實現了item大小,分區間隔,最小行間距,item之間的間隙大小
@protocol UICollectionViewDelegateFlowLayout <UICollectionViewDelegate>@optional- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
所以,自訂FlowLayout,並定義協議,以便定義這些方法。
@protocol WaterFlowLayoutDelegate <NSObject>//關鍵方法,此方法的作用是返回每一個item的size大小//資料中原始圖片大小- (CGSize)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;//分區間隔- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;//得到 item之間的間隙大小- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;//最小行間距- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;@end@interface WaterFlowLayout : UICollectionViewLayout//瀑布流一共多少列@property (nonatomic, assign) NSUInteger numberOfColumn;@property (nonatomic, assign) id<WaterFlowLayoutDelegate>delegate;
看圖可知,最小高的的item,將作為下一列item的起點。
@interface WaterFlowLayout ()//存放每一列的高度@property (nonatomic, retain) NSMutableArray *columnHeightsArray;//存放 每一個item的 屬性 包含 frame以及下標@property (nonatomic, retain) NSMutableArray *attributesArray;@end@implementation WaterFlowLayout//擷取最小高度的方法- (CGFloat)minHeight{ CGFloat min = 100000; for (NSNumber *height in _columnHeightsArray) { CGFloat h = [height floatValue]; if (min > h) { min = h; } } return min;}//擷取最大值- (CGFloat)maxHeight{ CGFloat max = 0; for (NSNumber *height in _columnHeightsArray) { CGFloat h = [height floatValue]; if (max < h) { max = h; } } return max;}//找到最小高的下標- (NSUInteger)indexOfMinHeight{ NSUInteger index = 0; for (int i = 0; i < [_columnHeightsArray count]; i ++) { CGFloat height = [_columnHeightsArray[i] floatValue]; if (height == [self minHeight]) { index = i; return index; } } return index;}//重寫父類的布局方法- (void)prepareLayout{ [super prepareLayout]; _attributesArray = [[NSMutableArray alloc] init]; _columnHeightsArray = [[NSMutableArray alloc] initWithCapacity:self.numberOfColumn]; //給列高數組裡面的對象賦初值 for (int i = 0; i < self.numberOfColumn; i ++) { [_columnHeightsArray addObject:@0.0]; } CGFloat totalWidth = self.collectionView.frame.size.width; //建立 每個item frame中的x、y CGFloat x = 0; CGFloat y = 0; NSUInteger itemCount = [self.collectionView numberOfItemsInSection:0]; for (int i = 0; i < itemCount; i ++) { //得到集合視圖中 列間隙的個數 NSUInteger numberOfSpace = self.numberOfColumn - 1; //代理對象執行代理方法,得到 item之間的間隙大小 CGFloat spaceWidth = [_delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:0]; //求每列的寬度,也就是每個item的width CGFloat width = (totalWidth - spaceWidth * numberOfSpace) / self.numberOfColumn; //擷取每一個itemSize的大小 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; //資料中原始圖片大小 CGSize imageSize = [_delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath]; //通過 約分公式得到固定寬之後的高度是多少 CGFloat height = width * imageSize.height / imageSize.width; UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; //記錄每一個item的大小和位置 attribute.frame = CGRectMake(x, y, width, height); //數組儲存每個item的位置資訊 [_attributesArray addObject:attribute]; NSLog(@"item = %d",i); NSLog(@"x = %.2f y = %.2f width = %.2f height = %.2f",x,y,width,height); //求列高最小的那一列的下標 NSUInteger minHeightIndex = [self indexOfMinHeight]; //求出最小列的高度 CGFloat minHeight = [_columnHeightsArray[minHeightIndex] floatValue]; //求出行高 CGFloat lineHeight = [_delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:0]; //上一次總的列高 加上 行高 加上新加上的item的height,才是現在這一列的總高度 //minHeight為最小列現在的高度 //lineHeight為行間距 //height為新加的item的高 _columnHeightsArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + lineHeight + height]; //重新算最小列高的下標 minHeightIndex = [self indexOfMinHeight]; //算下一次新加的item的x和y值 x = (spaceWidth + width) * minHeightIndex; y = [self minHeight]; }}//重寫這個方法,可以返回集合視圖的總高度- (CGSize)collectionViewContentSize{ return CGSizeMake(self.collectionView.frame.size.width, [self maxHeight]);}- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ return _attributesArray;}
注意,最後一個方法的實現,即- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect,如果這個方法不寫,集合視圖是顯示不出來的,這個方法是次儲存的每個item的資訊重新告訴集合視圖,進行顯示。