UI UICollectionView 单元格中的 CollectionView 以编程方式

UI CollectionView in UICollectionView Cell Programmatically

您好,我正在尝试使用 UICollectionView 创建一个像 facebook 这样的主页提要,但是在每个单元格中,我想放置另一个具有 3 个单元格的 collectionView。

you can clone the project here

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

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

但是删除那行给我带来了这个错误

The behavior of the UICollectionViewFlowLayout is not defined because: the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values

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

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

您的第一个问题将通过为 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

}()

第二个问题通过在 post 属性 的 OuterCell 的 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 class,如下所示:

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   {

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

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

然后添加需要的OuterCellProtocol方法来记录cell位置的变化,像这样:

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
}

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