简体   繁体   English

iOS / Swift:具有嵌入式UITableView的动态UITableViewCell行高不起作用

[英]iOS/Swift: Dynamic UITableViewCell row height with embedded UITableView not working

I have a UITableViewCell which contains a UICollectionView on top and a UITableView on the bottom. 我有一个UITableViewCell ,它的顶部包含一个UICollectionView ,底部包含一个UITableView The idea is that a dynamic amount of cells will be created in the inner UITableView and the parent UITableViewCell that encloses the two subviews will increase its height proportionally. 这个想法是,将在内部UITableView创建动态数量的单元格,并且将包围两个子视图的父级UITableViewCell会按比例增加其高度。

I am trying to take advantage of the estimatedRowHeight + UITableViewAutomaticDimention feature of the UITableViewCell that will allow the cell height to increase dynamically. 我试图把优势estimatedRowHeight + UITableViewAutomaticDimention的特征UITableViewCell ,使单元格高度动态增加。 However, it is not working. 但是,它不起作用。 It completely removes the embedded UITableView from view. 它将嵌入式UITableView从视图中完全删除。

比较实施

I have not made any constraints that limit the height of the enclosed UITableView , so I am not sure why it is not working. 我没有做出任何限制封闭UITableView高度的约束,所以我不确定为什么它不起作用。

约束上

Here is the implementation that attempts to make a dynamically sized UITableViewCell : 这是尝试制作动态大小的UITableViewCell

class OverviewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Enclosed Table View Example"
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 325 // Height for inner table view with 1 cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cell = tableView.dequeueReusableCell(withIdentifier: "appHeaderCell") as! AppHeaderCell

        return cell
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell

        return cell
    }
}

My only guess is that the constraint bottom = Inner Table View.bottom + 7 is causing the issue, but the entire view falls apart when this constraint is removed. 我唯一的猜测是,约束bottom = Inner Table View.bottom + 7引起了问题,但是当删除此约束时,整个视图就会崩溃。

What can I do to make the complex outer UITableViewCell dynamically adjust height based on the number of cells in the embedded UITableView ? 如何使复杂的外部UITableViewCell根据嵌入式UITableView的单元格数量动态调整高度?

Although it may seem like a good idea, the use of UITableViewAutomaticDimension in conjunction with estimatedRowHeight is not good to use in scenarios like this where we have general content inside table view cells. 尽管这似乎是一个好主意,但在这样的场景(我们在表格视图单元格中拥有常规内容)的情况下,将UITableViewAutomaticDimensionestimatedRowHeight高度结合使用并不是很好。 Making use of the heightForRowAt method, you can calculate the size of each individual cell before it centers the table. 利用heightForRowAt方法,您可以计算每个单个单元格在表格居中之前的大小。

Once we know how many cells will be in the inner table, you need to create an array whose elements correspond to the number inner cells that will ultimately determine the height of the outer cell, as all other content is constant. 一旦我们知道内部表中有多少个单元格,就需要创建一个数组,该数组的元素与内部单元格的数量相对应,这将最终确定外部单元格的高度,因为所有其他内容都是恒定的。

let cellListFromData: [CGFloat] = [3, 1, 4]

This array will give us the number of sections in our outer table view: 该数组将为我们提供外部表视图中的节数:

func numberOfSections(in tableView: UITableView) -> Int {
    return cellListFromData.count
}

We will convert each element in this array to a cell height in the following way: 我们将通过以下方式将该数组中的每个元素转换为像元高度:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let prototypeCell = tableView.dequeueReusableCell(withIdentifier: "appCell") as! AppCell

    let baseHeight = betweenCellSpacing + prototypeCell.innerCollectionView.contentSize.height + prototypeCell.innerTableView.sectionHeaderHeight + outerTableView.sectionHeaderHeight
    let dynamicHeight = prototypeCell.innerTableView.contentSize.height - prototypeCell.innerTableView.sectionHeaderHeight

    return baseHeight + (dynamicHeight * cellListFromData[indexPath.section])
}

That is, inside of the heightForRowAt method, we dequeue a prototype cell that will not be used in the resulting view (as dequeueReusableCell is not called inside cellForRowAt in this case). 也就是说,在heightForRowAt方法内部,我们使将不在结果视图中使用的原型单元出队(因为在这种情况下,在cellForRowAt内部未调用dequeueReusableCell )。 We use this prototype cell to extract information about what is constant and what is dynamic about the cell's content. 我们使用此原型单元提取有关单元内容的常量动态信息。 The baseHeight is the accumulated height of all the constant elements of the cell (plus the between-cell spacing) and the dynamicHeight is the height of an inner UITableViewCell . baseHeight是单元格中所有常量元素的累积高度(加上单元格之间的间隔),而dynamicHeight是内部UITableViewCell的高度。 The height of each cell then becomes baseHeight + dynamicHeight * cellListFromData[indexPath.section] . 然后,每个单元格的高度变为baseHeight + dynamicHeight * cellListFromData[indexPath.section]

Next, we add a numberOfCells variable to the class for the custom cell and set this in the cellForRowAt method in the main table view: 接下来,我们向自定义单元格的类中添加一个numberOfCells变量,并在主表视图的cellForRowAt方法中进行设置:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell

    cell.numberOfCells = Int(cellListFromData[indexPath.section])
    cell.innerTableView.reloadData()

    return cell
}

numberOfCells is set with the same cellListFromData that we used to get the height of the cell. 用我们用来获取单元格高度的相同cellListFromData设置numberOfCells Also, it is critical to call reloadData() on the inner table view after setting its number of cells so that we see that update in the UI. 同样, 设置内部单元格视图的单元格数之后 ,在内部表视图上调用reloadData()至关重要,这样我们才能在UI中看到该更新。

Here is the full code: 这是完整的代码:

class OverviewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var outerTableView: UITableView!
    let cellSpacing: CGFloat = 25
    let data: [CGFloat] = [3, 1, 4]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return data.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let prototypeCell = tableView.dequeueReusableCell(withIdentifier: "appCell") as! AppCell

        let baseHeight = cellSpacing + prototypeCell.innerCollectionView.contentSize.height + prototypeCell.innerTableView.sectionHeaderHeight + outerTableView.sectionHeaderHeight
        let dynamicHeight = prototypeCell.innerTableView.contentSize.height - prototypeCell.innerTableView.sectionHeaderHeight

        return baseHeight + (dynamicHeight * data[indexPath.section])
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "appCell", for: indexPath) as! AppCell

        cell.numberOfCells = Int(data[indexPath.section])
        cell.innerTableView.reloadData()

        return cell
    }
}

class AppCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var innerCollectionView: UICollectionView!
    @IBOutlet weak var innerTableView: UITableView!
    var numberOfCells: Int = 0

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numberOfCells
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cell = tableView.dequeueReusableCell(withIdentifier: "featureHeaderCell") as! BuildHeaderCell

        return cell
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "innerCell", for: indexPath) as! InnerCell

        return cell
    }
}

Methods relating to configuring the inner collection view is not included here as it is not related to the problem. 与配置内部集合视图有关的方法不在此处,因为它与问题无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM