Reputation: 6606
I have an iPad application that uses to UICollectionView
to display a grid of cells. The cells have the exact same width but different heights (as some have more text than others).
The only problem I am having is that the cells that are shorter, end up having extra spacing above/below.... this creates a bad looking collection view. It adds extra space, where no extra space is needed. I have tried to set the edge insets to 0 and the minimum line spacing to 0, but it still doesn't quite work.
Here is what my collection view looks like:
And here is what I want it to look like:
Here is my flow layout code:
[self.infoList registerClass:[InfoCell class] forCellWithReuseIdentifier:@"InfoCell"];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setItemSize:CGSizeMake(320, 160)];
[layout setScrollDirection:UICollectionViewScrollDirectionVertical];
[layout setMinimumInteritemSpacing:0];
[layout setMinimumLineSpacing:0];
[self.infoList setCollectionViewLayout:layout];
I am setting the edge insets to 0 as well:
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsZero;
}
How can I remove the extra unnecessary space? The only way I have found that works, is to make all the cells the exact same height, but that's not what I want. Some cells contains for text than others, so I have to give them extra height.
Upvotes: 0
Views: 593
Reputation: 12144
In my opinion, you can resolve this problem by custom UICollectionViewLayout
. You should take a look at Collection View Programming Guide for iOS.
With the code below, I assume we have 2 row column each row, items have same width and different height.
const CGFloat kPadding = 5.f;
const NSUInteger kNumberSmallCellALine = 2;
@interface CustomCollectionViewLayout : UICollectionViewLayout
@property (nonatomic, strong) NSMutableArray *layoutAttributes;
@property (nonatomic, assign) CGFloat contentHeight;
@end
@implementation CustomCollectionViewLayout
// Use this method to perform the up-front calculations needed to provide layout information.
- (void)prepareLayout{
[super prepareLayout];
if (!self.layoutAttributes){
self.layoutAttributes = [[NSMutableArray alloc] init];
// Calculate width of each item
CGFloat cellWidth = (self.collectionView.frame.size.width - (kNumberSmallCellALine - 1) * kPadding) / kNumberSmallCellALine;
// Default height of contentSize
self.contentHeight = 0.f;
// Height of column items on the left
CGFloat leftColumnHeight = 0.f;
// Height of column items on the right
CGFloat rightColumnHeight = 0.f;
for (int i = 0 ; i < [self.collectionView numberOfItemsInSection:0] ; i ++){
// Height of item at index i
CGFloat height = i * 20 + 20;
CGFloat xPos, yPos = 0.f;
if (i % 2 == 0) {
// If item on the right
xPos = 0.f;
yPos = leftColumnHeight;
leftColumnHeight += height + kPadding;
} else {
// Item on the left
xPos = cellWidth + kPadding;
yPos = rightColumnHeight;
rightColumnHeight += height + kPadding;
}
// Update height of contentSize after adding new item
self.contentHeight = MAX(leftColumnHeight, rightColumnHeight);
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
attr.frame = CGRectMake(xPos,
yPos,
cellWidth,
height);
[self.layoutAttributes addObject:attr];
}
}
}
// Use this method to return the attributes for cells and views that are in the specified rectangle.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray *currentAttributes = [NSMutableArray new];
for (UICollectionViewLayoutAttributes *attr in self.layoutAttributes) {
if (CGRectIntersectsRect(attr.frame, rect))
{
[currentAttributes addObject:attr];
}
}
return currentAttributes;
}
// Use this method to return the overall size of the entire content area based on your initial calculations.
- (CGSize)collectionViewContentSize{
return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight);
}
@end
Using
CustomCollectionViewLayout *layout = [[CustomCollectionViewLayout alloc] init];
[collectionView setCollectionViewLayout:layout];
It's a demo to resolve your issue. You should change the height of each item for being suitable with your situation.
For more information and easier understanding, you can check My Demo Repo
NOTE from @Supertecnoboff
You have to reload the layoutAttributes data if you are supporting multiple device orientations (eg: an iPad app). Otherwise the layout will be incorrect when rotating the device. You then need to call performBatchUpdates on the collection view to load in the new layout data.
Upvotes: 2
Reputation: 3960
I believe you have to create a custom layout.
In essence, it includes the overriding of following methods:
The prepareLayout
method will have following implementation, This is a crude implementation you may have to tweak it according to your need.
- (void) prepareLayout {
if (self.cache.count == 0) {
UIEdgeInsets insets = self.collectionView.contentInset;
self.contentWidth = self.collectionView.bounds.size.width - (insets.left + insets.right);
CGFloat columnWidth = self.contentWidth / self.numberOfColumns;
NSMutableArray *xOffsets = [NSMutableArray new];
NSMutableArray *yOffsets = [NSMutableArray new];
for (int i = 0; i < self.numberOfColumns; i++) {
[xOffsets addObject: @(i * columnWidth)];
[yOffsets addObject: @(0)];
}
int column = 0;
for (int i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
CGFloat cellHeight = arc4random_uniform(200);
CGFloat height = self.cellPadding * 2 + cellHeight;
CGRect frame = CGRectMake((CGFloat)[[xOffsets objectAtIndex:column] floatValue],
(CGFloat)[[yOffsets objectAtIndex:column] floatValue],
columnWidth,
height);
CGRect insetFrame = CGRectInset(frame, self.cellPadding, self.cellPadding);
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.frame = insetFrame;
[self.cache addObject:attributes];
self.contentHeight = MAX(self.contentHeight, CGRectGetMaxY(frame));
[yOffsets replaceObjectAtIndex:column withObject:@([[yOffsets objectAtIndex:column]floatValue] + height)];
column = column < (self.numberOfColumns - 1) ? (column + 1) : 0;
}
}
}
Find the gist of the Custom class.
Upvotes: 0