繁体   English   中英

具有动态高度的 UITableViewCell 内的 UITableView

[英]UITableView inside UITableViewCell with dynamic height

我需要将 UITableView 放在带有自动布局的 UITableViewCell 中,因为第二个表有不同的行数,而一行可以有不同的高度。
这是我的视图控制器

class ViewController: UIViewController {

    let tableView = UITableView()
    let cellId = "firstTableCellId"

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
        tableView.reloadData()
        view.backgroundColor = UIColor.gray
    }

    func setupView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.separatorStyle = .none
        tableView.register(NextTable.self, forCellReuseIdentifier: cellId)
        tableView.backgroundColor = UIColor.green
        tableView.separatorStyle = .singleLine

        view.addSubview(tableView)
        view.addConstraintsWithFormat("V:|-60-[v0]-5-|", views: tableView)
        view.addConstraintsWithFormat("H:|-8-[v0]-8-|", views: tableView)
    }
}

extension ViewController: UITableViewDelegate {

}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

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

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! NextTable
        cell.layoutIfNeeded()
        return cell
    }
}

和 NextTable 是第一个表格中的单元格

class NextTable: UITableViewCell {

    var myTableView: UITableView!
    let cellId = "nextTableCellId"

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = UIColor.brown
        setupView()
        myTableView.reloadData()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    let label: UILabel = {
        let label = UILabel()
        label.numberOfLines =  0
        label.lineBreakMode = .byWordWrapping
        label.text = "Next table:"
        label.textColor = UIColor.black
        label.sizeToFit()
        label.backgroundColor = UIColor.cyan
        return label
    }()

    func setupView() {
        myTableView = UITableView()
        myTableView.delegate = self
        myTableView.dataSource = self
        myTableView.separatorStyle = .singleLineEtched
        myTableView.backgroundColor = UIColor.blue
        myTableView.register(TableCell.self, forCellReuseIdentifier: cellId)
        myTableView.isScrollEnabled = false

        addSubview(myTableView)
        addSubview(label)
        addConstraintsWithFormat("H:|-30-[v0]-30-|", views: myTableView)
        addConstraintsWithFormat("H:|-30-[v0]-30-|", views: label)

        addConstraint(NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 15))
        addConstraint(NSLayoutConstraint(item: myTableView, attribute: .top, relatedBy: .equal, toItem: label, attribute: .bottom, multiplier: 1.0, constant: 0))
        addConstraint(NSLayoutConstraint(item: label, attribute: .bottom, relatedBy: .equal, toItem: myTableView, attribute: .top, multiplier: 1.0, constant: 0))
        addConstraint(NSLayoutConstraint(item: myTableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: -15))
    }
}

extension NextTable: UITableViewDelegate {

}

extension NextTable: UITableViewDataSource {

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

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

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

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! TableCell
        cell.layoutIfNeeded()
        return cell
    }
}

和第二个表格中的单元格

class TableCell: UITableViewCell {
    let label: UILabel = {
        let label = UILabel()
        label.numberOfLines =  0
        label.lineBreakMode = .byWordWrapping
        label.text = "Some text"
        label.textColor = UIColor.black
        label.sizeToFit()
        label.backgroundColor = UIColor.red
        return label
    }()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = UIColor.yellow
        setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setupView(){
        addSubview(label)
        addConstraintsWithFormat("V:|-8-[v0]-8-|", views: label)
        addConstraintsWithFormat("H:|-5-[v0]-5-|", views: label)
    }
}

这是我的代码的效果程序结果
没有第二个表,所以我创建新类并在单元格中将其用作新表视图

class InnerTableView: UITableView {
    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}

现在表格显示但尺寸太大InnerTableView 的结果
我该怎么做才能显示完整的第二个表格,而单元格底部没有空白空间。

我在表视图中添加了对属性的覆盖:

override var intrinsicContentSize: CGSize {
    self.layoutIfNeeded()
    return self.contentSize
}

你可以在我的GitHub 上找到表中表的完整代码

在您的第一个表格视图中,在您的单元格出列后将其添加到您的cellForRowAt (可能需要稍微调整以适应您的实现):

cell.tableView.reloadData()
DispatchQueue.main.async {
    cell.tableView.scrollToRow(at: IndexPath(row: cell.numberOfRowsInInnerTableView.count.count - 1, section: 0), at: .bottom, animated: false)
}
DispatchQueue.main.async {
    cell.tableView.invalidateIntrinsicContentSize()
    cell.tableView.layoutIfNeeded()
    self.updateHeight()
}

然后在同一个类(外表视图)中定义一个函数updateHeight

func updateHeight() {
    UIView.setAnimationsEnabled(false)
    tableView.beginUpdates()
    tableView.endUpdates()
    UIView.setAnimationsEnabled(true)
}

显然,这有点InnerTableView ,但本质上它允许单元格的InnerTableView知道所有内部单元格的实际高度并适当地调整外部 tableview 的大小。 这种方法对我有用。

您所需要的只是使 ParentTableView 的cellForRowAt indexPath 中的内容大小无效

parentCell.InsideTableview.invalidateIntrinsicContentSize()

我尝试了上述解决方案,但对我不起作用。 我做了一些小的改变,然后它对我来说很好。

A) 在我的 ViewController 中:

//注意:TestQuestionAnsOptionCell 是外层单元格

if let cell = tableView.dequeueReusableCell(withIdentifier: String(describing:TestQuestionAnsOptionCell.self), for: indexPath) as? TestQuestionAnsOptionCell {
            cell.reloadData()
            setUpInnerCellListner(cell: cell)
            cell.selectionStyle = .none
            return cell
  }


private func setUpInnerCellListner(cell:TestQuestionAnsOptionCell) {
    cell.reloadTable = { // Closure called from TestQuestionAnsOptionCell
        DispatchQueue.main.async {
            UIView.setAnimationsEnabled(false)
            self.tbQuestion.beginUpdates()
            self.tbQuestion.endUpdates()
            UIView.setAnimationsEnabled(true)
        }
    }
}

这里使用的扩展:

extension NSObject {
class var className: String {
    return String(describing: self)
}
}

B) 在 TestQuestionAnsOptionCell 中:

class TestQuestionAnsOptionCell: UITableViewCell {

var tblOptions: OwnTableView = OwnTableView()
var reloadTable:(()->(Void))?

override func awakeFromNib() {
    super.awakeFromNib()
    setupView()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

private func setupView() {
    tblOptions.estimatedRowHeight = 60.0
    tblOptions.rowHeight = UITableView.automaticDimension
    tblOptions.delegate = self
    tblOptions.dataSource = self
    tblOptions.separatorStyle = .none
    tblOptions.register(UINib(nibName: TestQuestionOptionCell.className, bundle: nil), forCellReuseIdentifier: TestQuestionOptionCell.className)
    tblOptions.isScrollEnabled = false
    
    addSubview(tblOptions)
    addConstraintsWithFormat("H:|-30-[v0]-30-|", views: tblOptions)
    
    addConstraint(NSLayoutConstraint(item: tblOptions, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 15))
    addConstraint(NSLayoutConstraint(item: tblOptions, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: -15))
}

func reloadData()  {
    tblOptions.reloadData()
}

}

使用 TableView 委托和数据源,我使用了以下内容:

extension TestQuestionAnsOptionCell:UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //NOTE: TestQuestionOptionCell is the inner cell

    if let cell = tableView.dequeueReusableCell(withIdentifier: TestQuestionOptionCell.className, for: indexPath) as? TestQuestionOptionCell {
        cell.updateCell()
        cell.selectionStyle = .none
        return cell
    }
    return UITableViewCell()
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
 
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    tblOptions.invalidateIntrinsicContentSize()
    tblOptions.layoutIfNeeded()
    reloadTable?()//Closure to update outer tableview
}

}

C) 内部 tableview 用作:

class OwnTableView: UITableView {
override var intrinsicContentSize: CGSize {
    self.layoutIfNeeded()
    return self.contentSize
}

override var contentSize: CGSize {
    didSet{
        self.invalidateIntrinsicContentSize()
    }
}
}

D) UIView 扩展为:

extension UIView {

func addConstraintsWithFormat(_ format: String, views: UIView...) {
    var viewsDictionary = [String:UIView]()
    for(index, view) in views.enumerated() {
        let key = "v\(index)"
        view.translatesAutoresizingMaskIntoConstraints = false
        viewsDictionary[key] = view
    }
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary ))
}
}

暂无
暂无

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

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