swift3 - uicollectionview教學 - uicollectionviewflowlayout




像SnapChat一樣的UICollectionView佈局? (2)

我們如何創建像SnapChat的故事一樣的UICollectionViewLayout?

有什麼想法嗎?

我想要一個沒有外部庫的解決方案。


UICollectionViewLayout就像SnapChat的故事一樣


Swift 3.2代碼

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]
    }

}

基於適合您的問題 的先例答案

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

    }
}

我將unitSize的高度設置為80,但是如果需要可以使用屏幕的大小,例如 _unitSize = CGSizeMake(size.width/2,size.height/4.);

那渲染:

旁注:由您自行調整邏輯或進行更改,單元框計算可能不是“最佳代碼”。