By JayVDiyk


2017-04-03 13:42:33 8 Comments

how do we create a UICollectionViewLayout like the SnapChat's stories?

Any ideas please?

enter image description here

I'd like to have a solution without external library.

2 comments

@Jatin Kathrotiya 2018-09-11 14:07:06

UICollectionViewLayout like the SnapChat's stories Like

enter image description here


Swift 3.2 Code

import Foundation

import UIKit

class StoryTwoColumnsLayout : UICollectionViewLayout {
    fileprivate var cache = [IndexPath: UICollectionViewLayoutAttributes]()
    fileprivate var cellPadding: CGFloat = 4
    fileprivate var contentHeight: CGFloat = 0
    var oldBound: CGRect!
    let numberOfColumns:Int = 2
    var cellHeight:CGFloat = 255

    fileprivate var contentWidth: CGFloat {
        guard let collectionView = collectionView else {
            return 0
        }
        let insets = collectionView.contentInset
        return collectionView.bounds.width - (insets.left + insets.right)
    }

    override var collectionViewContentSize: CGSize {
        return CGSize(width: contentWidth, height: contentHeight)
    }

    override func prepare() {
        super.prepare()
        contentHeight = 0
        cache.removeAll(keepingCapacity: true)
        guard cache.isEmpty == true, let collectionView = collectionView else {
            return
        }
        if collectionView.numberOfSections == 0 {
            return
        }

        let cellWidth = contentWidth / CGFloat(numberOfColumns)
        cellHeight = cellWidth / 720 * 1220
        var xOffset = [CGFloat]()
        for column in 0 ..< numberOfColumns {
            xOffset.append(CGFloat(column) * cellWidth)
        }
        var column = 0
        var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            var newheight = cellHeight
            if column == 0 {
             newheight = ((yOffset[column + 1] - yOffset[column]) > cellHeight * 0.3) ? cellHeight : (cellHeight * 0.90)
            }

            let frame = CGRect(x: xOffset[column], y: yOffset[column], width: cellWidth, height: newheight)
            let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = insetFrame
            cache[indexPath] = (attributes)
            contentHeight = max(contentHeight, frame.maxY)
            yOffset[column] = yOffset[column] + newheight
            if column >= (numberOfColumns - 1) {
                column = 0
            } else {
                column = column + 1
            }

        }
    }



    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
        // Loop through the cache and look for items in the rect
        visibleLayoutAttributes = cache.values.filter({ (attributes) -> Bool in
            return attributes.frame.intersects(rect)
        })
        print(visibleLayoutAttributes)
        return visibleLayoutAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        // print(cache[indexPath.item])
        return cache[indexPath]
    }

}

@Larme 2017-04-14 10:07:24

Based on a precedent answer adapted to your issue:

-(id)initWithSize:(CGSize)size
{
  self = [super init];
  if (self)
  {
      _unitSize = CGSizeMake(size.width/2,80);
      _cellLayouts = [[NSMutableDictionary alloc] init];
  }
  return self;
}
-(void)prepareLayout
{
    for (NSInteger aSection = 0; aSection < [[self collectionView] numberOfSections]; aSection++)
    {

        //Create Cells Frames
        for (NSInteger aRow = 0; aRow < [[self collectionView] numberOfItemsInSection:aSection]; aRow++)
        {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:aRow inSection:aSection];
            UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

            NSUInteger i = aRow%3;
            NSUInteger j = aRow/3;
            CGFloat offsetY = _unitSize.height*2*j;
            CGPoint xPoint;
            CGFloat height = 0;
            BOOL invert = NO;
            if (aRow%6 >= 3) //We need to invert Big cell and small cells => xPoint.x
            {
                invert = YES;
            }
            switch (i)
            {
                case 0:
                    xPoint = CGPointMake((invert?_unitSize.width:0), offsetY);
                    height = _unitSize.height;
                    break;
                case 1:
                    xPoint = CGPointMake((invert?_unitSize.width:0), offsetY+_unitSize.height);
                    height = _unitSize.height;
                    break;
                case 2:
                    xPoint = CGPointMake((invert?0:_unitSize.width), offsetY);
                    height = _unitSize.height*2;
                    break;
                default:
                    break;
            }

            CGRect frame = CGRectMake(xPoint.x, xPoint.y, _unitSize.width, height);
            [attributes setFrame:frame];
            [_cellLayouts setObject:attributes forKey:indexPath];
        }

    }
}

I set the height of unitSize to 80, but you can use the size of the screen if needed, like _unitSize = CGSizeMake(size.width/2,size.height/4.);.

That render: enter image description here

Side note: It's up to you to adapt the logic, or do changes, the cell frames calculation may not be the "best looking piece of code".

@JayVDiyk 2017-04-14 11:33:57

Hello, I haven't checked them out since I am not familiar with Obj C. It would be great if you could post some swift version, but thanks anyway!

@Jekil Patel 2017-10-05 12:03:47

@Larme, really it helps a lot. Thanks Again. +1 Voted

Related Questions

Sponsored Content

14 Answered Questions

11 Answered Questions

5 Answered Questions

1 Answered Questions

1 Answered Questions

[SOLVED] Easy UICollectionView Custom Layout

1 Answered Questions

TableView-Like UICollectionView Layout

1 Answered Questions

Stack UICollectionView sections

Sponsored Content