【问题标题】:UI CollectionView in UICollectionView Cell Programmatically以编程方式在 UICollectionView 单元中的 UI CollectionView
【发布时间】:2024-01-15 04:17:01
【问题描述】:

您好,我正在尝试使用 UICollectionView 制作像 facebook 这样的家庭供稿,但是在每个单元格中,我想放置另一个具有 3 个单元格的 collectionView。

you can clone the project here

我有两个错误,第一个是当我在内部集合上滚动时查看反弹不会将单元格带回中心。当我创建集合视图时,我启用了分页并将 minimumLineSpacing 设置为 0 我不明白为什么会这样。当我尝试调试时,我注意到当我删除此行时此错误停止

layout.estimatedItemSize = CGSize(width: cv.frame.width, height: 1)

但删除该行会给我带来这个错误

未定义 UICollectionViewFlowLayout 的行为是因为:item 高度必须小于 UICollectionView 的高度减去 section insets 顶部和底部值,减去 content insets 顶部和底部值

因为我的单元格具有动态高度 这是一个例子

我的第二个问题是每个内部单元格上的文本都显示好文本我必须滚动到内部集合视图的最后一个单元格才能看到这里显示的好文本是一个例子

【问题讨论】:

  • 那么第一个问题解决了吗?第二个问题是什么?
  • 没有一个固定在第一个 Gif 上,你可以看到最后单元格没有回到中心
  • 我的第二个问题是每个内部单元格上的文本都显示了好的文本我必须滚动到内部集合视图的最后一个单元格才能看到这里显示的好文本是一个例子,你可以看到第二个GIF
  • “好”文本是什么意思?
  • 我的意思是假设存在的文本。我有一个帖子数组,每个帖子都有一个字符串数组,所以要创建我使用 Posts.count 的所有外部单元格,然后我必须创建内部这样做的单元格我在 cellForItemAt indexPath 函数中使用 posts.listText.count 我做 cell.text = listText[indexPath.row] 但 indexPath.row 不是你可以在第二个 gif 上看到的索引路径我在 Lorem Ipsum 不是好之前在单元格上打印,但是在我滚动并返回后显示好一个

标签: ios swift uicollectionview uicollectionviewcell uicollectionviewflowlayout


【解决方案1】:

您的第一个问题将通过在 OuterCell 中为 innerCollectionView 设置 minimumInteritemSpacing 来解决。所以innerCollectionView的定义变成了这样:

let innerCollectionView : UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumLineSpacing = 0
    layout.minimumInteritemSpacing = 0

    let cv = UICollectionView(frame :.zero , collectionViewLayout: layout)
    cv.translatesAutoresizingMaskIntoConstraints = false
    cv.backgroundColor = .orange
    layout.estimatedItemSize =  CGSize(width: cv.frame.width, height: 1)
    cv.isPagingEnabled = true
    cv.showsHorizontalScrollIndicator = false

    return cv

}()

第二个问题通过在 OuterCell 的 post 属性的 didSet 中添加对 reloadData 和 layoutIfNeeded 的调用来解决,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}

您所看到的与单元重用有关。如果您滚动到第一项上的黄色边框文本,然后向下滚动,您可以看到这一点。你会看到其他人也在黄色边框的文本上(尽管现在至少有正确的文本)。

编辑

这里还有一种方法可以记住细胞的状态。

首先您需要跟踪位置何时发生变化,因此在 OuterCell.swft 中添加一个新协议,如下所示:

protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}

然后为该协议的委托添加一个实例变量到 OuterCell 类,如下所示:

public weak var delegate: OuterCellProtocol?

那么最后你需要添加以下方法,该方法在滚动完成时调用,计算新位置并调用委托方法让它知道。像这样:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if let index = self.innerCollectionView.indexPathForItem(at: CGPoint(x: self.innerCollectionView.contentOffset.x + 1, y: self.innerCollectionView.contentOffset.y + 1)) {
        self.delegate?.changed(toPosition: index.row, cell: self)
    }
}

这就是每个单元格检测集合视图单元格何时更改并通知委托人。让我们看看如何使用这些信息。

OutterCellCollectionViewController 将需要跟踪其集合视图中每个单元格的位置,并在它们变得可见时更新它们。

所以首先让 OutterCellCollectionViewController 符合 OuterCellProtocol 以便它在其中一个时被通知

class OutterCellCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, OuterCellProtocol   {

然后添加一个类实例变量来记录单元格位置到 OuterCellCollectionViewController,如下所示:

var positionForCell: [Int: Int] = [:]

然后添加所需的 OuterCellProtocol 方法来记录单元格位置变化,如下所示:

func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}

最后更新 cellForItemAt 方法以设置单元格的委托并使用新的单元格位置,如下所示:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}

如果您设法正确完成所有设置,它应该会在您向上和向下滚动列表时跟踪位置。

【讨论】:

  • 非常感谢您的帮助,但我尝试了您的代码,我有疑问您的解决方案确实有效,但我想知道这条线是否真的有用,因为我正在更新布局,它似乎没有用layout.estimatedItemSize = CGSize(width: cv.frame.width, height: 1) 另一个问题是,当我们在外部单元格上滚动一个时,其他随机单元格如何滚动到...。
  • 虽然不是绝对必要的,但为estimatedItemSize 提供某种合理的默认值始终是一种好习惯,这样做并没有什么坏处。文档说这样做有助于提高性能。
  • 你认为创建innerCell的2个子类并注册它们以防止重用更好吗?和滚动问题
  • 创建额外的子类不会阻止单元重用问题。当您向上和向下滚动列表时,您希望单元格记住它们是如何滚动集合视图的,还是它们都可以返回到第一个文本项?
  • 我希望他们记住他们在哪里