简体   繁体   English

将视图添加到UIStackView后,UITableViewCell不会更新高度

[英]UITableViewCell doesn't update height after adding a view to UIStackView

I have a UIStackView inside UITableViewCell 's contentView . 我在UITableViewCellcontentView有一个UIStackView Based on user interaction, I add/remove items in the UIStackView . 根据用户交互,我在UIStackView添加/删除项目。 After modifying the items in UIStackView , I expect the cell to update it's height accordingly. 修改UIStackView的项目后,我希望cell地更新它的高度。 But, it doesn't update it's height unless I call tableView.reloadData() . 但是,除非我调用tableView.reloadData()否则它不会更新它的高度。 But, calling reloadData() in cellForRowAtIndexPath / willDisplayCell becomes recursive. 但是,在cellForRowAtIndexPath / willDisplayCell调用reloadData()会变为递归。

What is the proper way to adjust the cell height at run time based on items in UIStackView ? 根据UIStackView项目,在运行时调整cell高度的正确方法是什么?

I use UITableViewAutomaticDimension 我使用UITableViewAutomaticDimension

Updating the Problem: 更新问题:

Here is a simple prototype of what I am trying to do. 是我想要做的简单原型。

My actual problem is dequeuing the cell. 我的实际问题是将单元格出列。

In the prototype, I have 2 reusable cells and 3 rows. 在原型中,我有2个可重复使用的单元格和3行。 For row 0 and 2, I dequeue cellA and for row 1, I dequeue cellB . 对于第0行和第2行,我使cellAcellA ,对于第1行,我使cellB出列cellB Below is the overview on the condition I use. 以下是我使用的条件的概述。

if indexPath.row == 0 {
    // dequeue cellA with 2 items in stackView
}

if indexPath.row == 1 {
    // dequeue cellB with 25 items in stackView
}

if indexPath.row == 2 {
    // dequeue cellA with 8 items in stackView
}

But the output is, row 0 contains 2 items in stackView - expected row 1 contains 25 items in stackView - expected row 2 contains 2 items in stackView - unexpected, row 0 is dequeued 但输出是,第0行包含stackView中的2个项目 - 预期第1行包含stackView中的25个项目 - 预期第2行包含stackView中的2个项目 - 意外,第0行已出列

I also tried removing all arranged subViews of stackView in cellForRowAtIndexPath . 我还尝试在cellForRowAtIndexPath删除所有已安排的stackView子stackView But, doing so, flickers the UI when scrolling. 但是,这样做会在滚动时闪烁UI。 How can I manage to get the desired output? 如何设法获得所需的输出?

Try to reload only the cell using: https://developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows 尝试使用以下命令仅重新加载单元格: https//developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows

Example code 示例代码

Here is an example. 这是一个例子。 We have basic table view cells (TableViewCell) inside a view controller. 我们在视图控制器中有基本的表视图单元(TableViewCell)。 The cells have 2 labels inside a stack view. 单元格在堆栈视图中有2个标签。 We can hide or show the second label using the collapse/reveal methods. 我们可以使用collapse / reveal方法隐藏或显示第二个标签。

class TableViewCell : UITableViewCell {
    @IBOutlet private var stackView: UIStackView!
    @IBOutlet private var firstLabel: UILabel!
    @IBOutlet private var secondLabel: UILabel!

    func collapse() {
        secondLabel.isHidden = true
    }

    func reveal() {
        secondLabel.isHidden = false
    }
}

class ViewController : UIViewController {

    @IBOutlet var tableView: UITableView!

    fileprivate var collapsedCells: Set<IndexPath> = []

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 128
    }

    @IBAction private func buttonAction(_ sender: Any) {
        collapseCell(at: IndexPath(row: 0, section: 0))
    }

    private func collapseCell(at indexPath: IndexPath) {
        if collapsedCells.contains(indexPath) {
            collapsedCells.remove(indexPath)
        } else {
            collapsedCells.insert(indexPath)
        }
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}


extension ViewController : UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
        if collapsedCells.contains(indexPath) {
            cell.collapse()
        } else {
            cell.reveal()
        }
        return cell
    }

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

I believe the problem is when you are adding views to the stack view. 我相信问题是您向堆栈视图添加视图时。

In general, adding elements should take place when the cell is initialized . 通常,在初始化单元格时应该添加元素。

willDisplay cell: is where one handles modifying attributes of cell contents. willDisplay cell:是处理修改单元格内容属性的地方。

If you move your code from willDisplay cell: to cellForRowAt indexPath: you should see a big difference. 如果您将代码从willDisplay cell:移动到cellForRowAt indexPath:您应该看到一个很大的区别。

I just made that one change to the code you linked to, and the rows are now auto-sizing based on the stack view contents. 我刚刚对您链接的代码进行了一次更改,现在行根据堆栈视图内容自动调整大小。

Edit: Looked at your updated code... the issue was still that you are adding your arrangedSubviews in the wrong place. 编辑:查看更新后的代码...问题仍然是您将arrangeSubviews添加到错误的位置。 And you compound it by calling reloadData(). 然后通过调用reloadData()来复合它。

Second Edit: Forgot to handle previously added subviews when the cells are reused. 第二次编辑:忘记在重复使用单元格时处理以前添加的子视图。

Updated code... replace your ViewController code with: 更新代码...将ViewController代码替换为:

//
//  ViewController.swift
//

import UIKit

class ViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = UIView()
        tableView.estimatedRowHeight = 56
    }

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

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell = UITableViewCell()

        if indexPath.row == 0 || indexPath.row == 2 {
            cell = tableView.dequeueReusableCell(withIdentifier: "cell")!

            if let stackView = cell.viewWithTag(999) as? UIStackView {
                let numberOfItemsInStackView = (indexPath.row == 0) ? 2 : 8
                let color = (indexPath.row == 0) ? UIColor.gray : UIColor.black

                // cells are reused, so clear out any previously added subviews...
                // but leave the first view that is part of the cell prototype
                while stackView.arrangedSubviews.count > 1 {
                    stackView.arrangedSubviews[1].removeFromSuperview()
                }

                // use "i" so we can count
                for i in 1...numberOfItemsInStackView {

                    // use label instead of view so we can number them for testing
                    let newView = UILabel()
                    newView.text = "\(i)"
                    newView.textColor = .yellow

                    // add a border, so we can see the frames
                    newView.layer.borderWidth = 1.0
                    newView.layer.borderColor = UIColor.red.cgColor

                    newView.backgroundColor = color
                    let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 54)
                    heightConstraint.priority = 999
                    heightConstraint.isActive = true
                    stackView.addArrangedSubview(newView)
                }
            }

        }

        if indexPath.row == 1 {
            cell = tableView.dequeueReusableCell(withIdentifier: "lastCell")!

            if let stackView = cell.viewWithTag(999) as? UIStackView {
                let numberOfItemsInStackView = 25

                // cells are reused, so clear out any previously added subviews...
                // but leave the first view that is part of the cell prototype
                while stackView.arrangedSubviews.count > 1 {
                    stackView.arrangedSubviews[1].removeFromSuperview()
                }

                // use "i" so we can count
                for i in 1...numberOfItemsInStackView {

                    // use label instead of view so we can number them for testing
                    let newView = UILabel()
                    newView.text = "\(i)"
                    newView.textColor = .yellow

                    // add a border, so we can see the frames
                    newView.layer.borderWidth = 1.0
                    newView.layer.borderColor = UIColor.red.cgColor

                    newView.backgroundColor = UIColor.darkGray
                    let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 32)
                    heightConstraint.priority = 999
                    heightConstraint.isActive = true
                    stackView.addArrangedSubview(newView)
                }
            }

        }

        return cell

    }

//    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
//        if cell.reuseIdentifier == "cell" {
//            if let stackView = cell.viewWithTag(999) as? UIStackView {
//                let numberOfItemsInStackView = (indexPath.row == 0) ? 2 : 8
//                let color = (indexPath.row == 0) ? UIColor.gray : UIColor.black
//                guard stackView.arrangedSubviews.count == 1 else { return }
//                for _ in 1...numberOfItemsInStackView {
//                    let newView = UIView()
//                    newView.backgroundColor = color
//                    let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 54)
//                    heightConstraint.priority = 999
//                    heightConstraint.isActive = true
//                    stackView.addArrangedSubview(newView)
//                }
//                tableView.reloadData()
//            }
//        }
//        
//        if cell.reuseIdentifier == "lastCell" {
//            if let stackView = cell.viewWithTag(999) as? UIStackView {
//                let numberOfItemsInStackView = 25
//                guard stackView.arrangedSubviews.count == 1 else { return }
//                for _ in 1...numberOfItemsInStackView {
//                    let newView = UIView()
//                    newView.backgroundColor = UIColor.darkGray
//                    let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 32)
//                    heightConstraint.priority = 999
//                    heightConstraint.isActive = true
//                    stackView.addArrangedSubview(newView)
//                }
//                tableView.reloadData()
//            }
//        }
//    }

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

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

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