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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 15:27:35