Swift 5.7中含可展开子TableView的父TableView单元格高度适配
实现带可展开折叠评论区的博客列表方案
针对你的需求,这里给出iOS端的具体实现思路和代码示例:
1. 父TableView单元格布局(含多行标签)
父单元格核心分为三个区域:
- 博客内容区:用
UILabel设置numberOfLines = 0,配合自动布局让内容自适应高度。 - 多行标签区:推荐用
UICollectionView实现流式布局,比多行UILabel更灵活。给布局设置estimatedItemSize = UICollectionViewFlowLayout.automaticSize,让标签根据文字长度自动调整宽度并换行。 - 评论容器区:用UIView包裹子TableView,初始折叠状态下仅显示子TableView的头部,隐藏所有评论行。
2. 子TableView的展开/折叠逻辑
给子TableView头部添加点击手势,实现状态切换:
- 在父单元格类中定义
isCommentsExpanded布尔属性标记展开状态。 - 头部点击时切换该属性值,重新加载子TableView数据:折叠时仅加载头部行,展开时加载头部+所有评论行。
- 状态切换后调用父TableView的
beginUpdates()和endUpdates(),触发当前单元格高度重计算。
3. 父TableView单元格高度自适应
要实现高度随子TableView状态动态调整,需做以下设置:
- 父TableView开启自动高度计算:
tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 400 // 设置接近实际的预估高度 - 给子TableView的高度约束添加IBOutlet,根据展开状态更新约束值:
- 折叠状态:高度 = 头部固定高度
- 展开状态:高度 = 头部高度 + 所有评论行高度之和
- 更新约束后触发父TableView刷新单元格高度。
核心代码示例
父单元格类(BlogCell.swift)
import UIKit class BlogCell: UITableViewCell { @IBOutlet weak var blogContentLabel: UILabel! @IBOutlet weak var tagsCollectionView: UICollectionView! @IBOutlet weak var commentsTableView: UITableView! @IBOutlet weak var commentsTableViewHeightConstraint: NSLayoutConstraint! var isCommentsExpanded = false { didSet { let headerHeight: CGFloat = 50 let commentsHeight = isCommentsExpanded ? CGFloat(comments.count * 60) : 0 commentsTableViewHeightConstraint.constant = headerHeight + commentsHeight commentsTableView.reloadData() // 通知父TableView更新高度 (superview?.superview as? UITableView)?.beginUpdates() (superview?.superview as? UITableView)?.endUpdates() } } var blog: BlogModel! var comments: [CommentModel] = [] override func awakeFromNib() { super.awakeFromNib() commentsTableView.delegate = self commentsTableView.dataSource = self commentsTableView.scrollEnabled = false // 禁用子TableView滚动,避免冲突 commentsTableView.register(UINib(nibName: "CommentHeaderCell", bundle: nil), forCellReuseIdentifier: "CommentHeaderCell") commentsTableView.register(UINib(nibName: "CommentCell", bundle: nil), forCellReuseIdentifier: "CommentCell") // 给头部添加点击手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toggleCommentsExpansion)) commentsTableView.tableHeaderView?.addGestureRecognizer(tapGesture) } @objc func toggleCommentsExpansion() { isCommentsExpanded.toggle() } } // MARK: - UITableView代理与数据源 extension BlogCell: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return isCommentsExpanded ? comments.count : 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentCell cell.comment = comments[indexPath.row] return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 60 } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerCell = tableView.dequeueReusableCell(withIdentifier: "CommentHeaderCell") as! CommentHeaderCell headerCell.titleLabel.text = "\(comments.count) 条评论" headerCell.expandIconImageView.image = UIImage(systemName: isCommentsExpanded ? "chevron.up" : "chevron.down") return headerCell.contentView } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } }
父TableView数据源方法
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "BlogCell", for: indexPath) as! BlogCell let blog = blogs[indexPath.row] cell.blog = blog cell.comments = blog.comments return cell }
注意事项
- 子TableView必须禁用滚动(
scrollEnabled = false),避免和父TableView的滚动逻辑冲突。 - 若评论数量过多,可限制子TableView最大高度,超过时开启内部滚动。
- 多行标签的CollectionView要设置正确的自动布局约束,确保自适应高度且不挤压其他控件。
内容的提问来源于stack exchange,提问作者Sanjay Borkakoty
相关产品推荐
相关产品推荐

