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的滚动功能,可手动计算高度并更新约束:
- 在
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计算实际高度 }
- 当子评论数据变化时,调用
updateSubTableHeight()并通知外部tableView刷新当前行:
// 在CommentCell中添加方法供外部调用 func refreshSubComments() { tableView.reloadData() updateSubTableHeight() }
- 在外部
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
相关产品推荐
相关产品推荐

