iOS - 如何使 UICollectionViewCell 根据其内容调整其高度?(包含 UITableView)

3 uitableview ios uicollectionviewcell swift intrinsic-content-size

我不知道为什么设计能够适应其内容的单元格如此复杂。它不应该需要那么多代码,我仍然不明白为什么UIKit不能正确处理这个问题。

无论如何,这是我的问题我已经编辑了整篇文章):

我有一个UICollectionViewCell包含UITableView.

这是我的sizeForItem方法:

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

        var cellWidth: CGFloat = collectionView.bounds.size.width 
        var cellHeight: CGFloat = 0

        let cellConfigurator = items[indexPath.item].cellConfigurator

        if type(of: cellConfigurator).reuseId == "MoonCollectionViewCell" {
            if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: cellConfigurator).reuseId, for: indexPath) as? MoonCollectionViewCell {
               cell.contentView.layoutIfNeeded()
               let size = cell.selfSizedTableView.intrinsicContentSize
               cellHeight = size.height
            }
        }

        return CGSize.init(width: cellWidth, height: cellHeight)
    }
Run Code Online (Sandbox Code Playgroud)

sizeForItem之前调用过cellForItem,这就是原因layoutIfNeeded,因为我无法得到正确的intrinsic content size

我已按照建议删除了 XIB,并在 Storyboard 中设计了我的 UICollectionViewCell。

这是我在 Storyboard 中设计的 UICollectionViewCell(只有 UITableViewCell 是在 XIB 文件中设计的)

我只在 UICollectionViewCell 中添加了一个 UITableView。
我希望UICollectionViewCell根据 的高度调整其大小tableView

现在这是我的tableView

我创建了一个子类UITableView(来自这篇文章

class SelfSizedTableView: UITableView {
    var maxHeight: CGFloat = UIScreen.main.bounds.size.height

    override func reloadData() {
        super.reloadData()
        self.invalidateIntrinsicContentSize()
        self.layoutIfNeeded()
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        return CGSize(width: contentSize.width, height: height)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我有disabled scrolling,我有dynamic prototypetableView单元格,样式是grouped

编辑:检查配置方法,它来自我用来以通用方式配置所有我的协议的协议UICollectionViewCell

func configure(data: [MoonImages]) {

        selfSizedTableView.register(UINib.init(nibName: "MoonTableViewCell", bundle: nil), forCellReuseIdentifier: "MoonTableViewCell")

        selfSizedTableView.delegate = self
        selfSizedTableView.dataSource = moonDataSource

        var frame = CGRect.zero
        frame.size.height = .leastNormalMagnitude
        selfSizedTableView.tableHeaderView = UIView(frame: frame)
        selfSizedTableView.tableFooterView = UIView(frame: frame)

        selfSizedTableView.maxHeight = 240.0
        selfSizedTableView.estimatedRowHeight = 40.0
        selfSizedTableView.rowHeight = UITableView.automaticDimension

        moonDataSource.data.addAndNotify(observer: self) { [weak self] in
            self?.selfSizedTableView.reloadData()
        }

        moonDataSource.data.value = data

    }
Run Code Online (Sandbox Code Playgroud)

仅供参考, 数据源是一个自定义数据源,具有动态值(泛型)和观察者模式,用于在设置数据时重新加载集合/表视图。

当我启动应用程序时,我也收到此警告。

[CollectionView] 在计算布局的过程中检测到更新布局信息的尝试(即可重入调用)。这将导致意外行为或崩溃。如果在调用委托时触发布局传递,则可能会发生这种情况。

关于我应该如何处理这个问题有任何提示或建议吗?

因为我面临一个奇怪的行为,就像我的 sizeForItem 使用随机值一样。高度与我的内在内容尺寸高度UICollectionViewCell不同。UITableView

如果我的 中有 2 行UITableView,则UICollectionView在这个大小下并不总是相等。我真的不知道如何实现这个目标......

我是不是该invalideLayout

All*_*Hsu 6

也许这不是你想要的答案,但这是我的两分钱。对于您的特定要求,更好的解决方案是放弃UITableView、 并使用UIStackView或您的自定义容器视图。

原因如下:

  1. UITableView是 的子类UIScrollView,但由于您已禁用其滚动功能,因此不需要UIScrollView.
  2. UITableView主要用于重用单元格,提高性能并使代码更加结构化。但由于您将其设置得与内容大小一样大,因此您的任何单元格都不会被重用,因此UITableView不会利用 的功能。

因此,实际上您不需要也不应该使用UITableViewUIScrollView来满足UICollectionViewCell您的要求。

如果您同意以上部分,以下是我们实践中的一些经验教训:

  1. 我们总是将大部分底层视图和代码逻辑(主要是数据组装)移动到UIView基于自定义视图中,而不是放入UITableViewCellUICollectionViewCell直接放入。然后将其添加到UITableViewCellor并设置约束UICollectionViewCellcontentView通过这个结构,我们可以在更多的场景中复用我们的自定义视图。
  2. 对于与您类似的需求,我们将创建一个工厂类来创建“行”,类似于您为 的创建“单元格”的方式UITableView,将它们添加到垂直的 中UIStackView,创建决定 的UIStackView宽度的约束。自动布局将处理剩下的事情。
  3. 在使用 时UICollectionViewCell,要计算所需的高度,在preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes)单元格的内部函数中,您可以使用contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)来计算高度,进行一些检查并返回。另外,请记住当宽度发生UICollectionView变化时使布局无效。