水平分页scrollview中缺少UICollectionView对齐逻辑

Dev*_*fly 57 iphone xcode cocoa-touch ios uicollectionview

我有一个UICollectionView,它工作正常,直到我开始滚动.这里有一些照片: 在此输入图像描述

你可以看到它很棒.当我开始滚动(启用分页)时,第一个在屏幕外显示: 在此输入图像描述

这就是问题.原始我的视图有3个视图,我想滚动并仅显示3个视图.但是当它滚动(启用分页)时,它隐藏了第一个视图的一点点,并显示下一页的下一个第一个视图的一点点.

这是一个视频,因为它有点难以解释: 问题的视频(Dropbox)

这是我的UICollectionView设置图片: 在此输入图像描述

如果有人可以提供帮助,那将会很棒!

Ji *_*ang 76

根本问题是Flow Layout不支持分页.要实现分页效果,您必须牺牲单元格之间的空间.并仔细计算单元格框架,并使其可以通过集合视图框架进行划分而不会留下余地.我会解释原因.

说出以下布局就是你想要的.

在此输入图像描述

请注意,最左边距(绿色)不是单元格间距的一部分.它由流动布局部分插入确定.由于流布局不支持异构间距值.这不是一项微不足道的任务.

因此,设置间距和插入后.您将获得以下布局.

在此输入图像描述

滚动到下一页后.你的细胞显然不符合你的预期.

在此输入图像描述

使单元格间隔为0可以解决此问题.但是,如果您希望页面上有额外的边距,则会限制您的设计,尤其是如果边距与单元格间距不同时.它还要求视图帧必须可被单元格框整除.有时,如果您的视图框架未固定(考虑旋转情况),则会很痛苦.

真正的解决方案是继承UICollectionViewFlowLayout并覆盖以下方法

- (CGSize)collectionViewContentSize
{
    // Only support single section for now.
    // Only support Horizontal scroll 
    NSUInteger count = [self.collectionView.dataSource collectionView:self.collectionView
                                               numberOfItemsInSection:0];

    CGSize canvasSize = self.collectionView.frame.size;
    CGSize contentSize = canvasSize;
    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
        NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;
        NSUInteger page = ceilf((CGFloat)count / (CGFloat)(rowCount * columnCount));
        contentSize.width = page * canvasSize.width;
    }

    return contentSize;
}


- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGSize canvasSize = self.collectionView.frame.size;

    NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
    NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;

    CGFloat pageMarginX = (canvasSize.width - columnCount * self.itemSize.width - (columnCount > 1 ? (columnCount - 1) * self.minimumLineSpacing : 0)) / 2.0f;
    CGFloat pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0f;

    NSUInteger page = indexPath.row / (rowCount * columnCount);
    NSUInteger remainder = indexPath.row - page * (rowCount * columnCount);
    NSUInteger row = remainder / columnCount;
    NSUInteger column = remainder - row * columnCount;

    CGRect cellFrame = CGRectZero;
    cellFrame.origin.x = pageMarginX + column * (self.itemSize.width + self.minimumLineSpacing);
    cellFrame.origin.y = pageMarginY + row * (self.itemSize.height + self.minimumInteritemSpacing);
    cellFrame.size.width = self.itemSize.width;
    cellFrame.size.height = self.itemSize.height;

    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        cellFrame.origin.x += page * canvasSize.width;
    }

    return cellFrame;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes * attr = [super layoutAttributesForItemAtIndexPath:indexPath];
    attr.frame = [self frameForItemAtIndexPath:indexPath];
    return attr;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * attrs = [NSMutableArray array];

    [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) {
        NSIndexPath * idxPath = attr.indexPath;
        CGRect itemFrame = [self frameForItemAtIndexPath:idxPath];
        if (CGRectIntersectsRect(itemFrame, rect))
        {
            attr = [self layoutAttributesForItemAtIndexPath:idxPath];
            [attrs addObject:attr];
        }
    }];

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

注意,上面的代码片段只支持单节和水平滚动方向.但它不难扩展.

此外,如果你没有数百万的细胞.缓存那些UICollectionViewLayoutAttributes可能是一个好主意.

  • 这是一个很好的答案,也是唯一一个适合我的方法.谢谢. (2认同)

Les*_*ary 28

您可以在UICollectionView上禁用分页,并使用自定义页面宽度/偏移量实现自定义水平滚动/分页机制,如下所示:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    float pageWidth = 210;

    float currentOffset = scrollView.contentOffset.x;
    float targetOffset = targetContentOffset->x;
    float newTargetOffset = 0;

    if (targetOffset > currentOffset)
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
    else
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;

    if (newTargetOffset < 0)
        newTargetOffset = 0;
    else if (newTargetOffset > scrollView.contentSize.width)
        newTargetOffset = scrollView.contentSize.width;

    targetContentOffset->x = currentOffset;
    [scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES];
}
Run Code Online (Sandbox Code Playgroud)


dev*_*vid 19

这个答案是迟到的,但我刚刚玩这个问题,发现漂移的原因是行间距.如果希望UICollectionView/FlowLayout以单元格宽度的精确倍数进行分页,则必须设置:

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
flowLayout.minimumLineSpacing = 0.0;
Run Code Online (Sandbox Code Playgroud)

您不会认为行间距在水平滚动中起作用,但显然它确实如此.

在我的情况下,我正在尝试从左到右分页,一次一个单元格,单元格之间没有空格.页面的每一个转弯都会从所需的位置引入一点点漂移,并且它似乎线性地累积. 每回合~10分.我意识到10.0是流布局中minimumLineSpacing的默认值.当我将其设置为0.0时,没有漂移,当我将其设置为边界宽度的一半时,每个页面都会移动额外的一半边界.

更改minimumInteritemSpacing没有影响.

编辑 - 从UICollectionViewFlowLayout的文档:

@property(nonatomic)CGFloat minimumLineSpacing;

讨论

...

对于垂直滚动网格,此值表示连续行之间的最小间距.对于水平滚动网格,此值表示连续列之间的最小间距. 此间距不应用于标题和第一行之间或最后一行和页脚之间的空格.

此属性的默认值为10.0.


nik*_*sky 9

在此输入图像描述

以下文章的解决方案优雅而简单.主要思想是在collectionView的顶部创建scrollView并传递所有contentOffset值.

http://b2cloud.com.au/tutorial/uiscrollview-paging-size/

应该通过实现这个方法来说:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;
Run Code Online (Sandbox Code Playgroud)

我没有像使用pagingEnabled = YES那样实现平滑动画.


Jon*_*han 7

我知道这个问题已经过时了,但对于碰巧偶然发现这个问题的人来说.

要纠正此问题,您需要将MinimumInterItemSpacing设置为0并减少内容的帧.


小智 6

我想我明白了这个问题.我会尝试让你理解它.

如果你仔细观察,那么你会看到这个问题只是逐渐发生,而不仅仅是在第一页滑动.

在此输入图像描述

如果我理解正确,在您的应用程序中,当前,每个UICollectionView项目都是我们看到的那些圆角框,并且您在所有这些项目之间有一些不变的偏移/边距.这就是造成这个问题的原因.

相反,你应该做的是创建一个UICollectionView项,它是整个视图宽度的1/3,然后在其中添加该圆形图像视图.要引用图像,绿色应该是您的UICollectionViewItem而不是黑色.


eri*_*kva 6

@devdavid在flowLayout.minimumLineSpacing上点亮为零.

它也可以在布局编辑器中完成,将行的最小间距设置为0:

更轻松的方式