效果图
LBAttributeLayout
场景
该布局的使用场景有很多:搜索记录
,商品sku, 一些标签等等
实现原理
UICollectionFlowLayout 是 在prepareLayout
创建布局属性,然后 通过
collectionViewContentSize 方法返回内容的size
通过layoutAttributesForElementsInRect 方法
返回各个item的布局的,
所以,我们自定义layout 的时候,需要重写这三个方法
-
(void)prepareLayout {
-
(CGSize)collectionViewContentSize {
} -
(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
}
核心逻辑
这种自适应宽度布局,最关键的是我们要给不同的
item设置不同的宽度,
其他的如高度,间隙等都可以通过属性来配置,只有宽度是动态的,
所以我们可以在 prepareLayout 中通过代理即时
获取某个item的宽度
关键代码
- (void)prepareLayout {
[super prepareLayout];
[self updateLayout];
}
- (void)updateLayout {
// 移除旧的布局
[self.layoutAttributesArray removeAllObjects];
self.contentSize = CGSizeMake(self.contentWidth, 0);
// 计算新的布局
NSInteger count = 0;
if ([self.collectionView.dataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]) {
count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
}
if (count > 0) {
CGFloat x = self.contentInset.left;
if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
x = self.contentSize.width - self.contentInset.left;
}
CGFloat y = self.contentInset.top;
for (int i = 0; i < count; i++) {
CGFloat currentX = x;
CGFloat currentY = y;
CGFloat cellWidth = [self cellWidthForItemAtIndex:i];
CGFloat cellHeight = self.cellHeight;
// 换行检测
if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionLeftToRight) {
if (currentX + cellWidth + self.contentInset.right > self.contentSize.width) {
currentX = self.contentInset.left;
currentY = currentY + cellHeight + self.cellVerticalSpace;
}
} else {
if (currentX - cellWidth - self.contentInset.right < 0) {
currentX = self.contentSize.width - self.contentInset.left;
currentY = currentY + cellHeight + self.cellVerticalSpace;
}
}
layoutAttributes.frame = CGRectMake(currentX, currentY, cellWidth, cellHeight);
if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
layoutAttributes.frame = CGRectMake(currentX - cellWidth, currentY, cellWidth, cellHeight);
}
[self.layoutAttributesArray addObject:layoutAttributes];
// 计算下一个item的origin,以及布局更新结束检测
if (i < count - 1) {
x = currentX + cellWidth + self.cellHorizontalSpace;
if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
x = currentX - cellWidth - self.cellHorizontalSpace;
}
y = currentY;
} else {
self.contentSize = CGSizeMake(self.contentWidth, currentY + cellHeight + self.contentInset.bottom);
}
}
}
}
使用方法
//创建 collectionView
- (UICollectionView *)collectionView
{
if (!_collectionView) {
LBAttributeLayout *layout = [[LBAttributeLayout alloc] initWithConfiguration:self.configuration];
layout.delegate = self;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 100, CGRectGetWidth(self.view.bounds), 200) collectionViewLayout:layout];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([UICollectionViewCell class])];
_collectionView.dataSource = self;
_collectionView.delegate = self;
}
return _collectionView;
}
#计算内容高度
NSMutableArray *widthArray = [NSMutableArray array];
for (int i = 0; i < self.dataSource.count; i ++) {
CGFloat width = [self cellWidthForItemAtIndex:i];
[widthArray addObject:@(width)];
}
CGFloat height = [LBAttributeLayout contentHeightWithConfiguration:self.configuration cellWidthArray:widthArray];
self.collectionView.frame = CGRectMake(0, 100, CGRectGetWidth(self.view.bounds), height);