【问题标题】:How do you vertically align the UICollectionViewCells in a UICollectionView?你如何垂直对齐 UICollectionView 中的 UICollectionViewCells?
【发布时间】:2026-02-22 01:20:09
【问题描述】:

我在水平滚动的UICollectionView 上有不同高度的单元格,它似乎垂直分布单元格,在单元格之间留下空白空间,我想将它们顶部对齐并在每个底部有可变的空白空间列。

【问题讨论】:

    标签: ios uicollectionview uicollectionviewlayout


    【解决方案1】:

    我扩展了提供给我的 UICollectionView 的 UICollectionViewFlowLayout。以下覆盖对我有用。

    #import "TopAlignedCollectionViewFlowLayout.h"
    
    const NSInteger kVerticalSpacing = 10;
    
    @implementation TopAlignedCollectionViewFlowLayout
    
    - (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];
        if (indexPath.item == 0) { 
            CGRect frame = currentItemAttributes.frame;
            frame.origin.y = sectionInset.top;
            currentItemAttributes.frame = frame;
    
            return currentItemAttributes;
        }
        NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
        CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
        CGFloat previousFrameRightPoint = previousFrame.origin.y + previousFrame.size.height + kVerticalSpacing;
        CGRect currentFrame = currentItemAttributes.frame;
        CGRect strecthedCurrentFrame = CGRectMake(currentFrame.origin.x,
                                                  0,
                                                  currentFrame.size.width,
                                                  self.collectionView.frame.size.height
                                                  );
        if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) {
            CGRect frame = currentItemAttributes.frame;
            frame.origin.y = frame.origin.y = sectionInset.top;
            currentItemAttributes.frame = frame;
            return currentItemAttributes;
        }
        CGRect frame = currentItemAttributes.frame;
        frame.origin.y = previousFrameRightPoint;
        currentItemAttributes.frame = frame;
        return currentItemAttributes;
    }
    
    @end
    

    【讨论】:

    • 布局的其他派生的伟大起点。像魅力一样工作!
    • 我尝试了这个解决方案,但它不适用于静态单元格宽度和动态单元格高度。流向是水平的。
    【解决方案2】:

    我将此移植到 Xamarin/MonoTouch 用于我的项目,并认为它可能有用

    public class TopAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout
    {
        const int VerticalSpacing = 10;
    
        public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(RectangleF rect)
        {
            var attributesToReturn = base.LayoutAttributesForElementsInRect(rect);
            foreach (UICollectionViewLayoutAttributes attributes in attributesToReturn)
            {
                if (attributes.RepresentedElementKind == null)
                {
                    var indexPath = attributes.IndexPath;
                    attributes.Frame = this.LayoutAttributesForItem(indexPath).Frame;
                }
            }
            return attributesToReturn;
        }
    
        public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath indexPath)
        {
            var currentItemAttributes = base.LayoutAttributesForItem(indexPath);
            UIEdgeInsets sectionInset = ((UICollectionViewFlowLayout) this.CollectionView.CollectionViewLayout).SectionInset;
    
            if (indexPath.Item == 0)
            {
                var frame1 = currentItemAttributes.Frame;
                frame1.Y = sectionInset.Top;
                currentItemAttributes.Frame = frame1;
    
                return currentItemAttributes;
            }
    
            var previousIndexPath = NSIndexPath.FromItemSection(indexPath.Item - 1, indexPath.Section);
            var previousFrame = this.LayoutAttributesForItem(previousIndexPath).Frame;
            var previousFrameRightPoint = previousFrame.Y + previousFrame.Size.Height + VerticalSpacing;
            var currentFrame = currentItemAttributes.Frame;
            var strecthedCurrentFrame = new RectangleF(currentFrame.X, 0, currentFrame.Size.Width, this.CollectionView.Frame.Size.Height);
            if (!previousFrame.IntersectsWith(strecthedCurrentFrame))
            {
                var frame = currentItemAttributes.Frame;
                frame.Y = frame.Y = sectionInset.Top;
                currentItemAttributes.Frame = frame;
                return currentItemAttributes;
            }
            RectangleF frame2 = currentItemAttributes.Frame;
            frame2.Y = previousFrameRightPoint;
            currentItemAttributes.Frame = frame2;
            return currentItemAttributes;
        }
    
    }
    

    【讨论】:

    • 这种作品(移植到OSX),但是我的物品没有下一行
    【解决方案3】:

    我想您正在使用 UICollectionViewFlowLayout 来布局您的单元格。我不认为你可以用流布局来做到这一点。您可能必须编写 UICollectionViewLayout 的自定义子类来对齐每个单元格的顶部边缘。

    这不是一个非常漂亮的解决方案,但是如果强制所有单元格大小相同并在单元格中设置约束以使内容与顶部对齐,该怎么办?这可以模拟您想要的外观,而无需继承 UICollectionViewLayout。

    【讨论】: