Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you vertically align the UICollectionViewCells in a UICollectionView?

I have cells of varying height on a horizontally scrolling UICollectionView that appears to evenly distribute the cells vertically leaving empty space in between the cells and I would like to top align them and have the variable empty space at the bottom of each column.

like image 226
Everus Avatar asked Jun 10 '13 21:06

Everus


2 Answers

I extended my UICollectionViewFlowLayout provided to my UICollectionView. The following overriding worked for me.

#import "TopAlignedCollectionViewFlowLayout.h"

const NSInteger kVerticalSpacing = 10;

@implementation TopAlignedCollectionViewFlowLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
        if (nil == attributes.representedElementKind) {
            NSIndexPath* indexPath = attributes.indexPath;
            attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
        }
    }
    return attributesToReturn;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes* currentItemAttributes =
    [super layoutAttributesForItemAtIndexPath:indexPath];
    UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout sectionInset];
    if (indexPath.item == 0) { 
        CGRect frame = currentItemAttributes.frame;
        frame.origin.y = sectionInset.top;
        currentItemAttributes.frame = frame;

        return currentItemAttributes;
    }
    NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
    CGFloat previousFrameRightPoint = previousFrame.origin.y + previousFrame.size.height + kVerticalSpacing;
    CGRect currentFrame = currentItemAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(currentFrame.origin.x,
                                              0,
                                              currentFrame.size.width,
                                              self.collectionView.frame.size.height
                                              );
    if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) {
        CGRect frame = currentItemAttributes.frame;
        frame.origin.y = frame.origin.y = sectionInset.top;
        currentItemAttributes.frame = frame;
        return currentItemAttributes;
    }
    CGRect frame = currentItemAttributes.frame;
    frame.origin.y = previousFrameRightPoint;
    currentItemAttributes.frame = frame;
    return currentItemAttributes;
}

@end
like image 198
Everus Avatar answered Oct 05 '22 23:10

Everus


I ported this to Xamarin/MonoTouch for my project and thought it could be useful

public class TopAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout
{
    const int VerticalSpacing = 10;

    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(RectangleF rect)
    {
        var attributesToReturn = base.LayoutAttributesForElementsInRect(rect);
        foreach (UICollectionViewLayoutAttributes attributes in attributesToReturn)
        {
            if (attributes.RepresentedElementKind == null)
            {
                var indexPath = attributes.IndexPath;
                attributes.Frame = this.LayoutAttributesForItem(indexPath).Frame;
            }
        }
        return attributesToReturn;
    }

    public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath indexPath)
    {
        var currentItemAttributes = base.LayoutAttributesForItem(indexPath);
        UIEdgeInsets sectionInset = ((UICollectionViewFlowLayout) this.CollectionView.CollectionViewLayout).SectionInset;

        if (indexPath.Item == 0)
        {
            var frame1 = currentItemAttributes.Frame;
            frame1.Y = sectionInset.Top;
            currentItemAttributes.Frame = frame1;

            return currentItemAttributes;
        }

        var previousIndexPath = NSIndexPath.FromItemSection(indexPath.Item - 1, indexPath.Section);
        var previousFrame = this.LayoutAttributesForItem(previousIndexPath).Frame;
        var previousFrameRightPoint = previousFrame.Y + previousFrame.Size.Height + VerticalSpacing;
        var currentFrame = currentItemAttributes.Frame;
        var strecthedCurrentFrame = new RectangleF(currentFrame.X, 0, currentFrame.Size.Width, this.CollectionView.Frame.Size.Height);
        if (!previousFrame.IntersectsWith(strecthedCurrentFrame))
        {
            var frame = currentItemAttributes.Frame;
            frame.Y = frame.Y = sectionInset.Top;
            currentItemAttributes.Frame = frame;
            return currentItemAttributes;
        }
        RectangleF frame2 = currentItemAttributes.Frame;
        frame2.Y = previousFrameRightPoint;
        currentItemAttributes.Frame = frame2;
        return currentItemAttributes;
    }

}
like image 37
Chris Koiak Avatar answered Oct 06 '22 00:10

Chris Koiak