You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS开发:嵌套在UITableViewCell内的UITableView不显示问题求助

嵌套UITableView子评论不显示问题解决

问题背景

要实现类似TikTok的评论结构,主评论单元格内嵌套UITableView展示子评论,但内部的UITableView无法显示内容,无约束报错,怀疑是行高计算导致。

相关代码

MainView 代码

final class CommentsView: BaseView {
    
    lazy var tableView = with(UITableView()) {
        $0.separatorStyle = .none
        $0.backgroundColor = .white
        $0.dataSource = self
        $0.delegate = self
        $0.register(item: CommentCell.self)
        $0.estimatedRowHeight = 50.0
        $0.rowHeight = UITableView.automaticDimension
    }
    private let sendMsgPanel = with(SendMessagePanelView()) {
        $0.backgroundColor = .white
    }
    
    
    override func setupUI() {
        super.setupUI()
        
        addSubviews([
            tableView,
            sendMsgPanel
        ])
    }
    
    override func setupConstraints() {
        super.setupConstraints()
        tableView.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(10)
            make.trailing.equalToSuperview()
            make.top.equalToSuperview().offset(20)
            make.bottom.equalTo(sendMsgPanel.snp.top)
        }
        sendMsgPanel.snp.makeConstraints { make in
            make.bottom.leading.trailing.equalToSuperview()
            make.height.equalTo(100)
        }
    }
    
    override func setupVisuals() {
        super.setupVisuals()
    }
}

extension CommentsView: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: CommentCell = tableView.deque(for: indexPath)
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    }
}

评论单元格及子评论单元格代码

final class CommentCell: UITableViewCell {
    
    private let userImageView = with(UIImageView()) {
        $0.image = UIImage(cgImage: Asset.placeholder.image.cgImage!)
        $0.contentMode = .scaleAspectFill
    }
    private let contentStackView = with(UIStackView.vertical) {
        $0.spacing = 5
    }
    private let nameLnameLabel = with(UILabel.proximaFont(ofSize: .small, weight: .regular)) {
        $0.text = "N L"
        $0.textColor = .gray
    }
    private let commentLabel = with(UILabel.proximaFont(ofSize: .regularSmall, weight: .regular)) {
        $0.text = "Comment"
        $0.textColor = .almostBlack
    }
    
    private let imageSize: CGFloat = 40
    
    private lazy var tableView = with(UITableView()) {
        $0.separatorStyle = .none
        $0.backgroundColor = .white
        $0.dataSource = self
        $0.delegate = self
        $0.register(item: SubCommentCell.self)
        $0.estimatedRowHeight = 50.0
        $0.rowHeight = UITableView.automaticDimension
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        setupUI()
        setupConstrains()
        setupVisual()
    }
    
    private func setupUI() {
        contentView.addSubviews([
            //userImageView,
            contentStackView,
            tableView
        ])
        contentStackView.addArrangedSubviews([
            nameLnameLabel,
            commentLabel,
        ])
    }
    
    private func setupConstrains() {
        contentStackView.snp.makeConstraints { make in
            make.leading.top.trailing.equalToSuperview()
        }
        tableView.snp.makeConstraints { make in
            make.top.equalTo(contentStackView.snp.bottom)
            make.leading.trailing.bottom.equalToSuperview()
        }
    }
    
    private func setupVisual() {
        selectionStyle = .none
        backgroundColor = .white
        userImageView.setRounded(cornerRadius: imageSize / 2)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

extension CommentCell: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: SubCommentCell = tableView.deque(for: indexPath)
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    }
}


final class SubCommentCell: UITableViewCell {
    
    private let label = with(UILabel()) {
        $0.text = "sub comment"
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        setupUI()
        setupConstrains()
        setupVisual()
    }
    
    private func setupUI() {
        contentView.addSubview(label)
    }
    
    private func setupConstrains() {
        label.positionToTheEdges()
    }
    
    private func setupVisual() {
    }
    
}

问题原因及解决方案

核心原因

外部CommentCell使用自动行高,但内部UITableView本身不会主动向父视图报告自身所需高度,导致外部cell的自动行高计算时忽略了内部tableView的高度,最终内部tableView被挤压至高度为0,无法显示。

具体解决步骤

方案1:禁用内部tableView滚动,让其自适应高度

这是最简单的解决方式,适合子评论数量不多的场景:

// 修改CommentCell中内部tableView的初始化代码
private lazy var tableView = with(UITableView()) {
    $0.separatorStyle = .none
    $0.backgroundColor = .white
    $0.dataSource = self
    $0.delegate = self
    $0.register(item: SubCommentCell.self)
    $0.estimatedRowHeight = 50.0
    $0.rowHeight = UITableView.automaticDimension
    $0.isScrollEnabled = false // 关键:禁止滚动,让tableView根据内容撑开高度
}

同时确保SubCommentCell的约束完整(当前代码中label已经贴边,没问题),外部CommentCell的自动行高会根据内部tableView的实际高度计算。

方案2:手动计算并绑定内部tableView高度约束

如果需要保留内部tableView的滚动功能,可手动计算高度并更新约束:

  1. 在CommentCell中添加高度约束的引用:
private var subTableHeightConstraint: Constraint!

private func setupConstrains() {
    contentStackView.snp.makeConstraints { make in
        make.leading.top.trailing.equalToSuperview()
    }
    // 初始化高度约束为0,后续动态更新
    subTableHeightConstraint = tableView.snp.height.equalTo(0).constraint
    tableView.snp.makeConstraints { make in
        make.top.equalTo(contentStackView.snp.bottom)
        make.leading.trailing.bottom.equalToSuperview()
        subTableHeightConstraint
    }
    
    // 计算子评论总高度并更新约束
    updateSubTableHeight()
}

// 添加更新高度的方法
private func updateSubTableHeight() {
    let rowCount = tableView.numberOfRows(inSection: 0)
    let rowHeight = tableView.estimatedRowHeight
    subTableHeightConstraint.update(offset: rowCount * rowHeight)
    // 若需要精确高度,可遍历cell计算实际高度
}
  1. 当子评论数据变化时,调用updateSubTableHeight()并通知外部tableView刷新当前行:
// 在CommentCell中添加方法供外部调用
func refreshSubComments() {
    tableView.reloadData()
    updateSubTableHeight()
}
  1. 在外部CommentsView中,当子评论数据更新时:
let cell = tableView.cellForRow(at: indexPath) as? CommentCell
cell?.refreshSubComments()
tableView.reloadRows(at: [indexPath], with: .automatic)

方案3:优化外部tableView的行高计算

在外部CommentsView的代理方法中手动计算每行高度:

extension CommentsView: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        // 计算主评论内容高度(示例值,可根据实际label内容计算)
        let mainCommentHeight = 60
        // 计算子评论总高度
        let subCommentCount = 3
        let subCommentHeight = subCommentCount * 50
        return CGFloat(mainCommentHeight + subCommentHeight)
    }
}

若主评论内容是动态的,可使用UILabel.sizeThatFits()计算实际高度:

// 示例:计算label的实际高度
let label = UILabel()
label.text = "Comment"
label.font = UIFont.proximaFont(ofSize: .regularSmall, weight: .regular)
label.numberOfLines = 0
let labelHeight = label.sizeThatFits(CGSize(width: tableView.bounds.width - 20, height: CGFloat.greatestFiniteMagnitude)).height

额外优化建议

嵌套UITableView容易引发性能问题,尤其是评论数量较多时,建议:

  • 使用UITableView的section实现展开折叠,将子评论放在主评论的section下;
  • 改用UICollectionView实现层级式评论布局,更灵活且性能更优;
  • 对评论列表实现懒加载或分页加载,避免一次性加载大量数据。

内容的提问来源于stack exchange,提问作者eokhjqcztkxkhoghbv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:09:52