How do you determine spacing between cells in UICollectionView flowLayout

I have a UICollectionView with a flow layout and each cell is a square. How do I determine the spacing between each cells on each row? I can't seem to find the appropriate settings for this. I see there's a min spacing attributes on the nib file for a collection view, but I set this to 0 and the cells doesn't even stick.

enter image description here

Any other idea?


Update: Swift version of this answer: https://github.com/fanpyi/UICollectionViewLeftAlignedLayout-Swift


Taking @matt's lead I modified his code to insure that items are ALWAYS left aligned. I found that if an item ended up on a line by itself, it would be centered by the flow layout. I made the following changes to address this issue.

This situation would only ever occur if you have cells that vary in width, which could result in a layout like the following. The last line always left aligns due to the behavior of UICollectionViewFlowLayout, the issue lies in items that are by themselves in any line but the last one.

With @matt's code I was seeing.

enter image description here

In that example we see that cells get centered if they end up on the line by themselves. The code below insures your collection view would look like this.

enter image description here

#import "CWDLeftAlignedCollectionViewFlowLayout.h"

const NSInteger kMaxCellSpacing = 9;

@implementation CWDLeftAlignedCollectionViewFlowLayout

- (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];

    CGRect currentFrame = currentItemAttributes.frame;

    if (indexPath.item == 0) { // first item of section
        currentFrame.origin.x = sectionInset.left; // first item of the section should always be left aligned
        currentItemAttributes.frame = currentFrame;

        return currentItemAttributes;
    }

    NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
    CGFloat previousFrameRightPoint = CGRectGetMaxX(previousFrame) + kMaxCellSpacing;

    CGRect strecthedCurrentFrame = CGRectMake(0,
                                              currentFrame.origin.y,
                                              self.collectionView.frame.size.width,
                                              currentFrame.size.height);

    if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line
        // the approach here is to take the current frame, left align it to the edge of the view
        // then stretch it the width of the collection view, if it intersects with the previous frame then that means it
        // is on the same line, otherwise it is on it's own new line
        currentFrame.origin.x = sectionInset.left; // first item on the line should always be left aligned
        currentItemAttributes.frame = currentFrame;
        return currentItemAttributes;
    }

    currentFrame.origin.x = previousFrameRightPoint;
    currentItemAttributes.frame = currentFrame;
    return currentItemAttributes;
}

@end

To get a maximum interitem spacing, subclass UICollectionViewFlowLayout and override layoutAttributesForElementsInRect: and layoutAttributesForItemAtIndexPath:.

For example, a common problem is this: the rows of a collection view are right-and-left justified, except for the last line which is left-justified. Let's say we want all the lines to be left-justified, so that the space between them is, let's say, 10 points. Here's an easy way (in your UICollectionViewFlowLayout subclass):

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

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* atts =
    [super layoutAttributesForItemAtIndexPath:indexPath];

    if (indexPath.item == 0) // degenerate case 1, first item of section
        return atts;

    NSIndexPath* ipPrev =
    [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];

    CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
    CGFloat rightPrev = fPrev.origin.x + fPrev.size.width + 10;
    if (atts.frame.origin.x <= rightPrev) // degenerate case 2, first item of line
        return atts;

    CGRect f = atts.frame;
    f.origin.x = rightPrev;
    atts.frame = f;
    return atts;
}

The reason this is so easy is that we aren't really performing the heavy lifting of the layout; we are leveraging the layout work that UICollectionViewFlowLayout has already done for us. It has already decided how many items go in each line; we're just reading those lines and shoving the items together, if you see what I mean.