如圖所示,通過實現不規則的網格分佈,來顯示出不同的效果。因為集合視圖必須要指定佈局還可以顯示,所以自定義佈局就可以實現瀑布流的效果。 //創建佈局對象 WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init]; flowLayout.d
如圖所示,通過實現不規則的網格分佈,來顯示出不同的效果。因為集合視圖必須要指定佈局還可以顯示,所以自定義佈局就可以實現瀑布流的效果。
//創建佈局對象 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的信息重新告訴集合視圖,進行顯示。