UICollectionView 布局像 SnapChat?

Posted

技术标签:

【中文标题】UICollectionView 布局像 SnapChat?【英文标题】:UICollectionView Layout like SnapChat? 【发布时间】:2017-04-03 13:42:33 【问题描述】:

我们如何像 SnapChat 的故事一样创建 UICollectionViewLayout?

有什么想法吗?

我想要一个没有外部库的解决方案。

【问题讨论】:

对于不使用Snapshat的人来说,单元格排列背后的逻辑是什么?我给了那里:***.com/questions/42364859/… 一些想法,关于如何做到这一点。您可能只需要安排框架的计算以满足您的需要。 相同的序列将在 exa 中重复。在第三行又是两个项目,在第四行又是 3 个项目? @Larme 我意识到我的截图是错误的,最终让你们感到困惑。对不起。我已经更新了截图。 我意识到我的截图是错误的,最终让你们感到困惑。对不起。我已经更新了截图。 @Swift_Guru @JayVDiyk 感谢您提供更好的解释。我发布了一些你可以使用的东西。 【参考方案1】:

基于a precedent answer 适应您的问题:

-(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.);

那个渲染:

旁注:调整逻辑或进行更改取决于您,单元格框架计算可能不是“最好看的代码”。

【讨论】:

您好,由于我对 Obj C 不熟悉,所以我没有检查过它们。如果您能发布一些 swift 版本,那就太好了,不过还是谢谢! @Larme,真的很有帮助。再次感谢。 +1 投票【参考方案2】:

UICollectionViewLayout 喜欢 SnapChat 的故事 Like

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]
    


【讨论】:

以上是关于UICollectionView 布局像 SnapChat?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 UICollectionView 自定义布局

UICollectionView 水平分页布局

UICollectionView中删除一个单元格后刷新单元格布局

如何以编程方式在 UICollectionView 上添加自动布局?

在布局更改的同时重新加载/动画 UICollectionView

UICollectionView - 垂直滚动,带有自定义布局的水平分页