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

时间:2017-06-28 10:10:45

标签: ios swift uitableview tableviewcell uistackview

UIStackView UITableViewCell内有contentView UIStackView。根据用户互动,我在UIStackView中添加/删除项目。修改cell中的项目后,我希望tableView.reloadData()相应地更新其高度。但是,除非我致电reloadData(),否则它不会更新它的高度。但是,在cellForRowAtIndexPath / willDisplayCell中调用cell会变为递归。

根据UIStackView中的项目,在运行时调整UITableViewAutomaticDimension高度的正确方法是什么?

我使用cellA

更新问题:

Here是我想要做的简单原型。

我的实际问题是将单元格出列。

在原型中,我有2个可重复使用的单元格和3行。对于第0行和第2行,我将cellB出列,对于第1行,我将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 } 出列。以下是我使用条件的概述。

stackView

但输出是, 第0行包含stackView中的2个项目 - 预期 第1行包含stackView中的25个项目 - 预期 第2行包含stackView中的2个项目 - 意外,第0行已出列

我还尝试删除cellForRowAtIndexPath中所有已排列的Exit Sub子视图。但是,这样做会在滚动时闪烁UI。如何设法获得所需的输出?

2 个答案:

答案 0 :(得分:2)

尝试使用:https://developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows

重新加载单元格

示例代码

这是一个例子。我们在视图控制器中有基本的表视图单元(TableViewCell)。单元格在堆栈视图中有2个标签。我们可以使用折叠/显示方法隐藏或显示第二个标签。

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
    }
}

答案 1 :(得分:1)

我相信问题是 时你是在堆栈视图中添加视图。

通常,当单元格初始化时,应该添加元素。

willDisplay cell:是处理修改单元格内容属性的地方。

如果您将代码从willDisplay cell:移至cellForRowAt indexPath:,您应该会看到很大的差异。

我刚刚对您链接的代码进行了一次更改,现在行根据堆栈视图内容自动调整大小。

修改:查看更新后的代码...问题仍在于您将您的arrangeSubviews添加到错误的位置。然后通过调用reloadData()来复合它。

第二次编辑:忘记在重复使用单元格时处理以前添加的子视图。

更新了代码...将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
    }
}