带分页的UICollectionView - 设置页面宽度

Gui*_*rme 22 ios uicollectionview

我有一个可以一次显示大约3.5个单元格的Collection View,我希望它能够启用分页.但我希望它能够捕捉到每个单元格(就像App Store应用程序一样),而不是滚动视图的整个宽度.我怎样才能做到这一点?

Mik*_*e M 51

另一种方法是创建自定义UICollectionViewFlowLayout并覆盖方法,如下所示:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)offset 
                                 withScrollingVelocity:(CGPoint)velocity {

    CGRect cvBounds = self.collectionView.bounds;
    CGFloat halfWidth = cvBounds.size.width * 0.5f;
    CGFloat proposedContentOffsetCenterX = offset.x + halfWidth;

    NSArray* attributesArray = [self layoutAttributesForElementsInRect:cvBounds];

    UICollectionViewLayoutAttributes* candidateAttributes;
    for (UICollectionViewLayoutAttributes* attributes in attributesArray) {

        // == Skip comparison with non-cell items (headers and footers) == //
        if (attributes.representedElementCategory != 
            UICollectionElementCategoryCell) {
            continue;
        }

        // == First time in the loop == //
        if(!candidateAttributes) {
            candidateAttributes = attributes;
            continue;
        }

        if (fabsf(attributes.center.x - proposedContentOffsetCenterX) < 
            fabsf(candidateAttributes.center.x - proposedContentOffsetCenterX)) {
            candidateAttributes = attributes;
        }
    }

    return CGPointMake(candidateAttributes.center.x - halfWidth, offset.y);

}
Run Code Online (Sandbox Code Playgroud)

https://gist.github.com/mmick66/9812223

  • 注意:当我们真正显示预览单元格时(即使它们的alpha值为0.0f),这将起作用.这是因为如果预览单元格在滚动时不可用,则它们的属性对象将不会在循环中传递...

如果您正在寻找Swift解决方案,我还创建了一个包含代码的小教程.


Jes*_*sak 17

您可以通过作为集合视图的委托并实现该方法来捕捉单元格:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
Run Code Online (Sandbox Code Playgroud)

这告诉您用户已完成拖动,并允许您修改targetContentOffset以与您的单元格对齐(即舍入到最近的单元格).请注意,您需要注意如何修改targetContentOffset; 特别是,你需要避免更改它,以便视图需要在传递速度的相反方向滚动,否则你会得到动画故障.如果你谷歌的方法名称,你可以找到很多这方面的例子.

  • @MikeM是的,这就是为什么它是一个inout参数; 你可以使用像`*targetContentOffset = CGPointMake(...)`这样的东西. (3认同)

Jon*_*iVR 13

这是我在Swift 5中针对基于单元格的垂直分页的实现:

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    guard let collectionView = self.collectionView else {
        let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        return latestOffset
    }

    // Page height used for estimating and calculating paging.
    let pageHeight = self.itemSize.height + self.minimumLineSpacing

    // Make an estimation of the current page position.
    let approximatePage = collectionView.contentOffset.y/pageHeight

    // Determine the current page based on velocity.
    let currentPage = velocity.y == 0 ? round(approximatePage) : (velocity.y < 0.0 ? floor(approximatePage) : ceil(approximatePage))

    // Create custom flickVelocity.
    let flickVelocity = velocity.y * 0.3

    // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
    let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

    let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - collectionView.contentInset.top

    return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset)
}
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • 不小故障
  • 设置为假!(否则将无法正常工作)
  • 使您可以轻松设置自己的轻弹速度
  • 如果尝试此操作后仍然itemSize无法解决问题,请检查您的尺寸是否与项目大小实际匹配,因为这通常是一个问题,尤其是在使用时collectionView(_:layout:sizeForItemAt:),请使用自定义变量来代替itemSize。
  • 设置时效果最佳self.collectionView.decelerationRate = UIScrollView.DecelerationRate.fast

这是水平版本(尚未进行全面测试,因此请原谅任何错误):

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    guard let collectionView = self.collectionView else {
        let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        return latestOffset
    }

    // Page width used for estimating and calculating paging.
    let pageWidth = self.itemSize.width + self.minimumInteritemSpacing

    // Make an estimation of the current page position.
    let approximatePage = collectionView.contentOffset.x/pageWidth

    // Determine the current page based on velocity.
    let currentPage = velocity.x == 0 ? round(approximatePage) : (velocity.x < 0.0 ? floor(approximatePage) : ceil(approximatePage))

    // Create custom flickVelocity.
    let flickVelocity = velocity.x * 0.3

    // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
    let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

    // Calculate newHorizontalOffset.
    let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - collectionView.contentInset.left

    return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y)
}
Run Code Online (Sandbox Code Playgroud)

这段代码基于我在个人项目中使用的代码,您可以在此处下载并运行Example目标,以检出该代码。

  • 谢谢!您的解决方案是唯一可以正常工作的解决方案!我必须用自定义计算替换itemSize,因为itemSize仅在不按照https://developer.apple.com/documentation/uikit/uikit/uicollectionviewflowlayout/1617711-itemsize使用collectionView(_:layout:sizeForItemAt :)时使用 (2认同)
  • 由于人们在使用此方法时可能正在使用collectionView(_:layout:sizeForItemAt :),因此最好将itemSize的使用替换为其他方法。 (2认同)