UICollectionView 中的单元格顺序
Posted
技术标签:
【中文标题】UICollectionView 中的单元格顺序【英文标题】:Cells order in UICollectionView 【发布时间】:2017-02-21 10:33:17 【问题描述】:我需要使用不同大小(1x1、2x2、2x1、3x2.5)的单元格创建一个UICollectionView
。我已经编写了代码来根据它们使用的大小添加单元格collectionView:layout:sizeForItemAtIndexPath:
。
这是预期结果(单元格 1 和 3 必须位于单元格 2 的左侧):
但目前的结果是:
我的(丑陋的)当前代码是(self.cellSize = screen width / 3):
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
CGFloat size = self.cellSize;
if (indexPath.item == 0)
return CGSizeMake(self.cellSize * 3.f, self.cellSize * 2.5f);
if (indexPath.item == 1)
return CGSizeMake(self.cellSize, self.cellSize);
if (indexPath.item == 2)
return CGSizeMake(self.cellSize * 2.f, self.cellSize * 2.f);
if (indexPath.item == 3)
return CGSizeMake(self.cellSize, self.cellSize);
if (indexPath.item == 4)
return CGSizeMake(self.cellSize * 2.f, self.cellSize);
if (indexPath.item == 5)
return CGSizeMake(self.cellSize, self.cellSize);
if (indexPath.item == 6)
return CGSizeMake(self.cellSize, self.cellSize);
if (indexPath.item == 7)
return CGSizeMake(self.cellSize * 2.f, self.cellSize);
if (indexPath.item == 8)
return CGSizeMake(self.cellSize * 3.f, self.cellSize * 2.5f);
return CGSizeMake(size, size);
有没有办法指定单元格 3 必须在单元格 1 上方,但在单元格 2 的左侧?
我不想做一个不会改变的静态布局,因为每个单元格将来可能有不同的大小。例如,单元格 2 可以是 2x1 大小...
这样做的最佳方法是什么?我期待为UICollectionViewLayout
指定一个流程(如“从顶部”),但它不是那样工作的......
【问题讨论】:
我认为你必须继承UICollectionViewLayout
的子类,你会给每个单元格一个框架。
【参考方案1】:
继承 UICollectionViewLayout 的示例。 所有值都是硬编码的,只是为了明确其背后的逻辑。当然,这可以优化。
@interface CustomCollectionViewLayout ()
@property (nonatomic, strong) NSMutableDictionary *cellLayouts;
@property (nonatomic, assign) CGSize unitSize;
@end
@implementation CustomCollectionViewLayout
-(id)initWithSize:(CGSize)size
self = [super init];
if (self)
_unitSize = CGSizeMake(size.width/3,150);
_cellLayouts = [[NSMutableDictionary alloc] init];
return self;
-(void)prepareLayout
for (NSInteger i = 0; i < [[self collectionView] numberOfItemsInSection:0]; i ++)
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame;
switch ([indexPath item])
case 0:
frame = CGRectMake(0, 0, _unitSize.width*3, _unitSize.height*2.5);
break;
case 1:
frame = CGRectMake(0, _unitSize.height*2.5, _unitSize.width, _unitSize.height);
break;
case 2:
frame = CGRectMake(_unitSize.width, _unitSize.height*2.5, _unitSize.width*2, _unitSize.height*2);
break;
case 3:
frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height, _unitSize.width, _unitSize.height);
break;
case 4:
frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height, _unitSize.width*2, _unitSize.height);
break;
case 5:
frame = CGRectMake(_unitSize.width*2, _unitSize.height*2.5+_unitSize.height+_unitSize.height, _unitSize.width, _unitSize.height);
break;
case 6:
frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width, _unitSize.height);
break;
case 7:
frame = CGRectMake(_unitSize.width, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width*2, _unitSize.height);
break;
case 8:
frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width*3, _unitSize.height*2.5);
break;
default:
frame = CGRectZero;
break;
[attributes setFrame:frame];
[[self cellLayouts] setObject:attributes forKey:indexPath];
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
NSMutableArray *retAttributes = [[NSMutableArray alloc] init];
for (NSIndexPath *anIndexPath in [self cellLayouts])
UICollectionViewLayoutAttributes *attributes = [self cellLayouts][anIndexPath];
if (CGRectIntersectsRect(rect, [attributes frame]))
[retAttributes addObject:attributes];
return retAttributes;
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
return [self cellLayouts][indexPath];
-(CGSize)collectionViewContentSize
return CGSizeMake(_unitSize.width*3, _unitSize.height*9);
@end
然后,您只需调用:
CustomCollectionViewLayout *layout = [[CustomCollectionViewLayout alloc] initWithSize:self.myCollectionView.bounds.frame.size];
[self.myCollectionView setCollectionViewLayout:layout];
渲染:
【讨论】:
哇!它完美地工作。非常感谢你。我将尝试重写一些硬编码部分。 @Larme,真的很有帮助。再次感谢,+1 投票【参考方案2】:这与 Larme 建议的逻辑相同。但使用较少的硬代码,您可以在不添加新的case:
的情况下设置所需的项目数量。
我将“模式”称为一组 5 个项目。所以首先我定义常量值:
#define MAX_COLUMN 3 // Max columns in the pattern
#define MAX_LINE_PER_PATTERN 3 // Max lines in the pattern
#define PATTERN_ITEM_COUNT 5 // Max items in the pattern
然后我创建一个自定义布局,其中包含两个属性 NSMutableArray *layoutAttributes
和 CGFloat contentHeight
,我需要在其中覆盖方法:
- (void)prepareLayout
[super prepareLayout];
if (!self.layoutAttributes)
self.layoutAttributes = [[NSMutableArray alloc] init];
CGFloat cellWidth = self.collectionView.frame.size.width / MAX_COLUMN;
CGFloat cellHeight = cellWidth;
self.contentHeight = 0.f;
for (int item = 0 ; item < [self.collectionView numberOfItemsInSection:0] ; item ++)
CGFloat width, height = 0.f;
CGFloat xPos, yPos = 0.f;
NSInteger patternCount = (NSInteger)((CGFloat)item / (CGFloat)PATTERN_ITEM_COUNT);
NSInteger currentIndex = item % PATTERN_ITEM_COUNT;
switch (currentIndex)
case 0:
xPos = 0.f;
yPos = 0.f + MAX_LINE_PER_PATTERN * cellHeight * patternCount;
width = cellWidth;
height = cellHeight;
self.contentHeight += cellHeight;
break;
case 1:
xPos = cellWidth;
yPos = 0.f + MAX_LINE_PER_PATTERN * cellHeight * patternCount;
width = cellWidth * 2.f;
height = cellHeight * 2.f;
self.contentHeight += cellHeight;
break;
case 2:
xPos = 0.f;
yPos = cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount;
width = cellWidth;
height = cellHeight;
break;
case 3:
xPos = 0.f;
yPos = 2.f * cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount;
width = cellWidth * 2.f;
height = cellHeight;
self.contentHeight += cellHeight;
break;
case 4:
xPos = 2.f * cellWidth;
yPos = 2.f * cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount;
width = cellWidth;
height = cellHeight;
break;
default:
NSLog(@"error with index");
break;
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:item inSection:0]];
attr.frame = CGRectMake(xPos,
yPos,
width,
height);
[self.layoutAttributes addObject:attr];
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
NSMutableArray *currentAttributes = [NSMutableArray new];
for (UICollectionViewLayoutAttributes *attr in self.layoutAttributes)
if (CGRectIntersectsRect(attr.frame, rect))
[currentAttributes addObject:attr];
return currentAttributes;
- (CGSize)collectionViewContentSize
return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight);
然后将此自定义布局分配给self.collectionView.collectionViewLayout
,就是这样。您可以找到更多信息和快速版本here。
【讨论】:
以上是关于UICollectionView 中的单元格顺序的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView.reloadData() 更改单元格顺序 | iOS 斯威夫特