先讲一下思路。
如果要用UIScrollView实现瀑布流效果应该怎么做呢?
- 第一步当然是把UIScrollView初始化并放到目标View上
2.你需要知道 要放多少个子view 到UIScrollView上
3.你需要知道 每个子view的frame
4.根据这些frame 计算出ScrollView的contentSize
That's all! 是不是很简单
UICollectionview的布局是由专门的类UICollectionViewLayout 来实现,我们通用的UICollectionViewFlowLayout无法实现瀑布流效果。 要简单实现瀑布流效果,只需要自定义一个继承与UICollectionViewLayout的子类, 并实现几个重要的方法(这里并不按调用顺序来讲)
1.- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
UICollectionViewLayoutAttributes是布局属性,这里包含了你需要的所有布局信息
根据每个cell的indexPath生成对应的布局属性对象,在这里这个cell的位置信息存放到attribute里 将并返回给UICollectionView
注意生成UICollectionViewLayoutAttributes 不能用alloc init方法创建, 而应该用[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath] 获得, 否则会闪退
2、 - (void)prepareLayout
该方法会在UICollectionView呈现 刷新 或改变时调用
可以在里边实现一些初始化的操作, 遍历1方法,将获得的每个cell的attribute保存到一个特定数组attributeList里
3.- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
返回这个属性数组 attributeList
4.- (CGSize)collectionViewContentSize
返回整个页面的contentSize, 可以根据上面的attributeList算出
上代码
- (void)prepareLayout
{
[super prepareLayout];
[self.frameList removeAllObjects];
[self.attrList removeAllObjects];
for (NSMutableArray *arr in self.fList) {
[arr removeAllObjects];
}
NSInteger count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
for (int index = 0; index < count; index++) {
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
NSValue *frameValue = [NSValue valueWithCGRect:attr.frame];
CGFloat y = CGRectGetMinY(frameValue.CGRectValue);
NSInteger i = (y - 10) / (h + 10);
[self.frameList addObject:frameValue];
[self.attrList addObject:attr];
}
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSInteger index = indexPath.item;
NSInteger i = index % colume;
NSInteger j = index / colume;
CGFloat y, w;
__block CGFloat x;
if (index < colume) {
y = 10 + i * (h + 10);
} else {
NSValue *f1 = [self.fList[0] lastObject];
NSValue *f2 = [self.fList[1] lastObject];
NSValue *f3 = [self.fList[2] lastObject];
CGFloat x1 = CGRectGetMaxX([f1 CGRectValue]);
CGFloat x2 = CGRectGetMaxX([f2 CGRectValue]);
CGFloat x3 = CGRectGetMaxX([f3 CGRectValue]);
if (x1 <= x2 && x1 <= x3) {
y = CGRectGetMinY([f1 CGRectValue]);
i = 0;
} else if (x2 <= x1 && x2 <= x3) {
y = CGRectGetMinY([f2 CGRectValue]);
i = 1;
} else {
y = CGRectGetMinY([f3 CGRectValue]);
i = 2;
}
}
// x = 10 + i * (w + 10);
w = arc4random() % 200 + 50;
x = 10;
if (j == 0) {
} else {
[self.fList[i] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CGFloat ch = CGRectGetWidth([(NSValue *)obj CGRectValue]);
x += ch + 10;
}];
}
attr.frame = CGRectMake(x, y, w, h);
NSLog(@"index = %ld x = %lf, y = %lf, w = %lf, h = %lf", index, x, y, w, h);
return attr;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrList;
}
- (CGSize)collectionViewContentSize {
CGRect lastFrame = [(NSValue *)[self.frameList lastObject] CGRectValue];
return CGSizeMake(CGRectGetMaxX(lastFrame), 0);
}