Take the 2-minute tour ×

I'm trying to add UIViews between my UICollectionViewCells in my UICollectionView and I don't know how I could do that. I'm trying to accomplish something like this:


I'll probably need to write a custom UICollectionViewLayout, but I don't really know where to start.

share|improve this question
add comment

4 Answers

up vote 13 down vote accepted

I studied more of how UICollectionViewLayouts work and figured out how to solve it. I have an UICollectionReusableView subclass called OrangeView that will be positioned between my views, than I wrote an UICollectionViewFlowLayout subclass called CategoriesLayout that will deal with my layout.

Sorry for the big block of code, but here is how it looks like:

@implementation CategoriesLayout

- (void)prepareLayout {
    // Registers my decoration views.
    [self registerClass:[OrangeView class] forDecorationViewOfKind:@"Vertical"];
    [self registerClass:[OrangeView class] forDecorationViewOfKind:@"Horizontal"];

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    // Prepare some variables.
    NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];

    UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];

    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    CGRect baseFrame = cellAttributes.frame;
    CGRect nextFrame = nextCellAttributes.frame;

    CGFloat strokeWidth = 4;
    CGFloat spaceToNextItem = 0;
    if (nextFrame.origin.y == baseFrame.origin.y)
        spaceToNextItem = (nextFrame.origin.x - baseFrame.origin.x - baseFrame.size.width);

    if ([decorationViewKind isEqualToString:@"Vertical"]) {
        CGFloat padding = 10;

        // Positions the vertical line for this item.
        CGFloat x = baseFrame.origin.x + baseFrame.size.width + (spaceToNextItem - strokeWidth)/2;
        layoutAttributes.frame = CGRectMake(x,
                                            baseFrame.origin.y + padding,
                                            baseFrame.size.height - padding*2);
    } else {
        // Positions the horizontal line for this item.
        layoutAttributes.frame = CGRectMake(baseFrame.origin.x,
                                            baseFrame.origin.y + baseFrame.size.height,
                                            baseFrame.size.width + spaceToNextItem,

    layoutAttributes.zIndex = -1;
    return layoutAttributes;

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *baseLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * layoutAttributes = [baseLayoutAttributes mutableCopy];

    for (UICollectionViewLayoutAttributes *thisLayoutItem in baseLayoutAttributes) {
        if (thisLayoutItem.representedElementCategory == UICollectionElementCategoryCell) {
            // Adds vertical lines when the item isn't the last in a section or in line.
            if (!([self indexPathLastInSection:thisLayoutItem.indexPath] ||
                  [self indexPathLastInLine:thisLayoutItem.indexPath])) {
                UICollectionViewLayoutAttributes *newLayoutItem = [self layoutAttributesForDecorationViewOfKind:@"Vertical" atIndexPath:thisLayoutItem.indexPath];
                [layoutAttributes addObject:newLayoutItem];

            // Adds horizontal lines when the item isn't in the last line.
            if (![self indexPathInLastLine:thisLayoutItem.indexPath]) {
                UICollectionViewLayoutAttributes *newHorizontalLayoutItem = [self layoutAttributesForDecorationViewOfKind:@"Horizontal" atIndexPath:thisLayoutItem.indexPath];
                [layoutAttributes addObject:newHorizontalLayoutItem];

    return layoutAttributes;


I also wrote a category with some methods to check if an index path is the last in a line, in the last line or the last in a section:

@implementation UICollectionViewFlowLayout (Helpers)

- (BOOL)indexPathLastInSection:(NSIndexPath *)indexPath {
    NSInteger lastItem = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
    return  lastItem == indexPath.row;

- (BOOL)indexPathInLastLine:(NSIndexPath *)indexPath {
    NSInteger lastItemRow = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
    NSIndexPath *lastItem = [NSIndexPath indexPathForItem:lastItemRow inSection:indexPath.section];
    UICollectionViewLayoutAttributes *lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItem];
    UICollectionViewLayoutAttributes *thisItemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];

    return lastItemAttributes.frame.origin.y == thisItemAttributes.frame.origin.y;

- (BOOL)indexPathLastInLine:(NSIndexPath *)indexPath {
    NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];

    UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];

    return !(cellAttributes.frame.origin.y == nextCellAttributes.frame.origin.y);


And this is the final result:

Final Result

share|improve this answer
awesome, good design! –  nik Feb 17 at 12:04
When a cell is removed from the bottom, this code doesn't remove the separator, is there a way to do that? –  Kronusdark Feb 24 at 14:30
@Kronusdark try calling -invalidateLayout on the collection view layout to see if it forces redrawing everything. I haven't tested it, not sure if it works… –  MarcoPompei Feb 24 at 14:38
That works, but it hangs out for a second, I will have to play with the timing methinks. Thank you very much for the quick reply –  Kronusdark Feb 24 at 14:40
@Kronusdark Yeah, I thought it could happen. I checked the UICollectionViewLayout Class Reference, check the Methods to Override, six of them concerns adding and removing items, implementing them should work.Maybe I'll try to update this code when I get home, but I can't promise you anything. –  MarcoPompei Feb 24 at 14:50
add comment

If you're using sections and the layout is appropriate for it, you might use section headers and footers.

But based on your illustration, it looks like you just need to define the UICollectionViewCell to contain those views. So, where you register your class:

[collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];

put those border images in the UICollectionView cell subclass (in the above case, "CollectionViewCell"). That seems like the easiest approach.

Here's one I use:

- (id)initWithFrame:(CGRect)frame
    self = [super initWithFrame:frame];
    if (self)
        self.restorationIdentifier = @"Cell";
        self.backgroundColor = [UIColor clearColor];
        self.autoresizingMask = UIViewAutoresizingNone;
        const CGFloat borderWidth = 3.0f;
        UIView *bgView = [[UIView alloc] initWithFrame:frame];
        bgView.layer.borderColor = [UIColor blackColor].CGColor;
        bgView.layer.borderWidth = borderWidth;
        bgView.layer.cornerRadius = 6.0f;
        self.selectedBackgroundView = bgView;
    return self;
share|improve this answer
Sections aren't appropriated. I need to know things like if this is the last cell on this line I shouldn't display the line on the right. I want a Collection View that could be rotated and maybe even reused on the iPad, where the distance between cells would be different. –  MarcoPompei Apr 1 '13 at 19:37
You can control the spacing and size of the layouts (regardless of target device or orientation) through the methods of UICollectionViewDelegateFlowLayout protocol. If you structure the cell correctly, there is no reason you can't control the border views in any way you wish based on the indexPath of the cell. –  RegularExpression Apr 1 '13 at 23:57
add comment

Looks like if your collectionView background was green and contentView white you could get the horizontals with a space between the cells minimumLineSpacing. The vertical gap would be the tricky part, but if you were creative with your contentView and set the minimumInteritemSpacing carefully you could get it.

share|improve this answer
That won't work if there are items missing in the last row –  Joris Mans Dec 23 '13 at 12:10
add comment

Wow. That's a lot of code in the other answers just for a separator line between rows..

This is how I solve it. First you'll need to add the line separator inside the cell. Make sure you keep dragging it making it wider than the actual cell width so if your cell width is 60p your separator line will be 70.

@implementation CollectionViewController
    NSArray *test;
    int currentLocInRow;

inside cellForItemAtIndexPath:

if((indexPath.row+1) % 4 == 0)
        cell.clipsToBounds = YES;
        cell.clipsToBounds = NO;

    if([test count] - indexPath.row+1 < 4 - currentLocInRow)
        cell.lineSeparator.alpha = 0;
        cell.lineSeparator.alpha = 1;

enter image description here

If you want a separator at the end of the collection view but there's a chance that you won't get 4 cells at the last row you can add a simple Collection Reusable View as Footer.

share|improve this answer
The trickiest part is getting separators between items inside the same line and getting it to work independent of the container size (when you rotate or on an iPad). –  MarcoPompei Dec 11 '13 at 17:55
@MarcoPompei I only needed to implement lines under the rows for the app I was working on. If i'll ever need to add separators between the items I'll post my solution here (if i'll find a better one..) Great question. –  Segev Dec 11 '13 at 18:01
add comment

Your Answer


