如何确定UICollectionView flowLayout中单元格之间的间距

adi*_*dit 50 iphone objective-c ipad ios uicollectionview

我有一个流式布局的UICollectionView,每个单元格都是一个正方形.如何确定每行上每个单元格之间的间距?我似乎找不到适合的设置.我看到nib文件中有一个min间距属性用于集合视图,但我将其设置为0并且单元格甚至没有粘贴.

在此输入图像描述

还有其他想法吗?

Chr*_*ner 71

更新:这个答案的Swift版本:https://github.com/fanpyi/UICollectionViewLeftAlignedLayout-Swift


@ matt为首,我修改了他的代码,以确保项目始终保持对齐.我发现如果一个项目本身就在一条线上,它将以流布局为中心.我做了以下更改来解决此问题.

如果您的单元格宽度不同,则可能会出现这种情况,这可能会导致如下所示的布局.由于行为的原因,最后一行始终保持对齐UICollectionViewFlowLayout,问题在于任何行中的项目,而不是最后一行.

我看到@ matt的代码.

在此输入图像描述

在那个例子中,我们看到细胞如果它们自己最终在线上就会居中.下面的代码可确保您的集合视图看起来像这样.

在此输入图像描述

#import "CWDLeftAlignedCollectionViewFlowLayout.h"

const NSInteger kMaxCellSpacing = 9;

@implementation CWDLeftAlignedCollectionViewFlowLayout

- (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) { // first item of section
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item of the section should always be left aligned
        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.x + previousFrame.size.width + kMaxCellSpacing;

    CGRect currentFrame = currentItemAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(0,
                                              currentFrame.origin.y,
                                              self.collectionView.frame.size.width,
                                              currentFrame.size.height);

    if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line
        // the approach here is to take the current frame, left align it to the edge of the view
        // then stretch it the width of the collection view, if it intersects with the previous frame then that means it
        // is on the same line, otherwise it is on it's own new line
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item on the line should always be left aligned
        currentItemAttributes.frame = frame;
        return currentItemAttributes;
    }

    CGRect frame = currentItemAttributes.frame;
    frame.origin.x = previousFrameRightPoint;
    currentItemAttributes.frame = frame;
    return currentItemAttributes;
}

@end
Run Code Online (Sandbox Code Playgroud)


mat*_*att 32

要获得最大的interitem间距,请继承UICollectionViewFlowLayout并覆盖layoutAttributesForElementsInRect:layoutAttributesForItemAtIndexPath:.

例如,一个常见的问题是:集合视图的行是左右对齐的,除了左对齐的最后一行.假设我们希望所有的线都是左对齐的,所以它们之间的空间就是10点.这是一种简单的方法(在您的UICollectionViewFlowLayout子类中):

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray* arr = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* atts in arr) {
        if (nil == atts.representedElementKind) {
            NSIndexPath* ip = atts.indexPath;
            atts.frame = [self layoutAttributesForItemAtIndexPath:ip].frame;
        }
    }
    return arr;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* atts =
    [super layoutAttributesForItemAtIndexPath:indexPath];

    if (indexPath.item == 0) // degenerate case 1, first item of section
        return atts;

    NSIndexPath* ipPrev =
    [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];

    CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
    CGFloat rightPrev = fPrev.origin.x + fPrev.size.width + 10;
    if (atts.frame.origin.x <= rightPrev) // degenerate case 2, first item of line
        return atts;

    CGRect f = atts.frame;
    f.origin.x = rightPrev;
    atts.frame = f;
    return atts;
}
Run Code Online (Sandbox Code Playgroud)

这很容易的原因是我们并没有真正完成布局的繁重工作; 我们正在利用UICollectionViewFlowLayout已经为我们完成的布局工作.它已经决定了每行中有多少项; 如果你明白我的意思,我们只是阅读那些线条并将物品推到一起.

  • 感谢您的解决方案。一个小问题:如果有一个每行有大量水平项的集合视图(collectionView.contentSize.width 远大于单元格宽度),则此代码中的递归可能会很昂贵。 (2认同)

Cal*_*leb 16

有几点需要考虑:

  1. 尝试更改IB中的最小间距,但将光标留在该字段中.请注意,Xcode不会立即将文档标记为已更改.但是,当您单击其他字段时,Xcode会注意到文档已更改并在文件导航器中将其标记.因此,请务必在进行更改后选项卡或单击其他字段.

  2. 进行更改后保存storyboard/xib文件,并确保重建应用程序.不难错过这一步,然后你会不知道为什么你的改变似乎没有任何影响.

  3. UICollectionViewFlowLayout有一个minimumInteritemSpacing属性,这是你在IB中设置的.但是集合的委托也可以有一种方法来确定项目间距.该方法特朗普是布局的属性,因此如果您在委托中实现它,则不会使用布局的属性.

  4. 请记住,那里的间距是最小间距.布局将使用该数字(无论是来自属性还是来自委托方法)作为最小允许空间,但如果线上有剩余空间,则可以使用更大的空间.因此,例如,如果将最小间距设置为0,则可能仍会在项目之间看到一些像素.如果您想要更精确地控制项目的间距,您应该使用不同的布局(可能是您自己创建的一个).

  • 谢谢你的回答.我认为我看的更多是maximumInteritemSpacing,是否有这样的价值? (2认同)
  • 不,没有最大值.如果你考虑流布局的作用,你会明白为什么.流量布局将尽可能多的项目放在一条线上,同时仍然遵循最小项目间距.此时,将剩下*n*个像素,其中*n*大于或等于0且小于下一个项目的大小加上最小间距.然后布局采用那些n个像素并将它们均匀地分布到行上的项目.如果设置最大间距,则无法执行此操作.但是,您可以根据自己的喜好将自己的布局编写到空间项目中. (2认同)

小智 5

一点数学知识就更容易解决这个问题。Chris Wagner 编写的代码非常糟糕,因为它调用了前面每个项目的布局属性。所以你滚动得越多,速度就越慢......

只需使用这样的模(我也使用我的minimumInteritemSpacing值作为最大值):

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    NSInteger numberOfItemsPerLine = floor([self collectionViewContentSize].width / [self itemSize].width);

    if (indexPath.item % numberOfItemsPerLine != 0)
    {
        NSInteger cellIndexInLine = (indexPath.item % numberOfItemsPerLine);

        CGRect itemFrame = [currentItemAttributes frame];
        itemFrame.origin.x = ([self itemSize].width * cellIndexInLine) + ([self minimumInteritemSpacing] * cellIndexInLine);
        currentItemAttributes.frame = itemFrame;
    }

    return currentItemAttributes;
}
Run Code Online (Sandbox Code Playgroud)

  • 在我的情况下,前一个单元格的大小很重要,这就是为什么它需要前一个单元格的布局属性。我没有在大型集合视图上对其进行性能测试。我的用例最多有 11 个单元,所以这从来都不是问题。 (2认同)