滚动到 CollectionView 底部时,Collection View Cells 会无意中调整大小

Posted

技术标签:

【中文标题】滚动到 CollectionView 底部时,Collection View Cells 会无意中调整大小【英文标题】:Collection View Cells are resizing unintentionally when scrolling to bottom of CollectionView 【发布时间】:2019-11-07 20:55:34 【问题描述】:

我正在构建图像的集合视图(图库)。

布局是每行 3 个单元格。

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 

        let width: CGFloat = (view.frame.width / 3) - 8
        let height: CGFloat = width

        return CGSize(width: width, height: height)
    

在我滚动到集合视图的底部之前,一切看起来都很棒。

它来自: 3 per row, looks good

到: super blown up picture, larger than the screen

我也收到此错误消息:

The behavior of the UICollectionViewFlowLayout is not defined because:

the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values. 

Please check the values returned by the delegate.

The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fa97f708c70>, and it is attached to <UICollectionView: 0x7fa981022000; frame = (0 0; 414 896); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x6000019986f0>; layer = <CALayer: 0x6000017a8820>; contentOffset: 0, 498.33333333333331; contentSize: 414, 1250; adjustedContentInset: 88, 0, 34, 0; layout: <UICollectionViewFlowLayout: 0x7fa97f708c70>; dataSource: <MarsRover.GalleryCollectionVC: 0x7fa97f50b610>>.

任何见解都会很棒,我也想把它变成无限滚动(api 预取),如果这意味着我可以忽略它,仅供参考。

【问题讨论】:

您是否尝试将您的收藏视图项目宽度计算更改为基于collectionView 而不是view.frame.width?比如:let width: CGFloat = (collectionView.frame.width / 3) - 8. 谢谢,奥兰多,但我试过 view.bounds.width、view.frame.width、view.frame.size.width、cv.frame.size.width、cv.frame.width等等…… 【参考方案1】:

您得到的错误是指向项目的宽度作为问题,也就是说,基本上项目的宽度和间距不能超过集合视图的宽度。这是因为您的集合视图具有垂直滚动功能。

因此,要为您的集合视图实现正确的行为,您必须将您的控制器设置为集合视图的delegate,并采用UICollectionViewDelegateFlowLayout。在您的情况下,我可以看到您已经实现了 collectionView(_:, layout:, sizeForItemAt: 方法。

在您的实现中,有一个明确的意图是将 collectionView 的宽度分成三个相等的部分。该计算考虑了self.view.width 减去八的第三部分。如果我假设正确,您打算将项目间距保留为 8。如果是这种情况,您应该将其指定为另一种方法:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat 
    return 8

这会将项目间的间距指定为 8 个点。

继续使用单元格的widthheight,然后您必须除以collectionView.frame.width 但在此之前您必须从该数量中减去间距值,因为那是 剩余单元格空间。

所以你的实现将是

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    // We subtract 16 because there are 2 inter item spaces like:
    // [CELL] [spacing:8] [CELL] [spacing:8] [CELL]
    let usableWidth = collectionView.frame.width - (2 * 8) 

    let cellWidth: CGFloat = usableWidth / 3
    let cellHeight: CGFloat = cellWidth

    return CGSize(width: cellWidth, height: cellHeight)

这应该为你做布局。

【讨论】:

【参考方案2】:

由于所有项目的大小都相同,因此您不应该在委托方法中设置它们。而是在 viewDidLayoutSubviews 中的 flowLayout 上设置一个静态大小(您可以从情节提要中拖动一个插座),并使用布局插图来执行此操作,这样您就不会弄错数学:

    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
        let remainingSpace = collectionView.bounds.width
                            - flowLayout.sectionInset.left
                            - flowLayout.sectionInset.right
                            - flowLayout.minimumInteritemSpacing * (Constant.numberOfItemsAcross - 1)
        let dimension = remainingSpace / Constant.numberOfItemsAcross
        flowLayout.itemSize = CGSize(width: dimension, height: dimension)
    

【讨论】:

【参考方案3】:

首先,为什么不使用 bounds 而不是 frame

其次,发生这种情况的原因很可能是因为您正在其他地方调整布局,而 collectionView 正在加载单元格(滚动将使其加载单元格)。

你是否使用 UIScrollViewDelegate 方法来处理布局?

【讨论】:

以上是关于滚动到 CollectionView 底部时,Collection View Cells 会无意中调整大小的主要内容,如果未能解决你的问题,请参考以下文章

滚动到collectionView的底部不起作用

首次加载时显示collectionView的底部,无需滚动和延迟加载

滚动时应用 CollectionView layerMask “抖动”

Collectionview 的底部约束未在运行时更新

CollectionView 标题在滚动时停止刷新/重新加载数据

奇怪的错误,collectionView 视图滚动到倒数第二条消息,当试图滚动到底部