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

嵌套UITableView滚动后数据丢失问题排查求助

嵌套UITableView滚动后数据丢失问题求助

嵌套UITableView初始加载数据正常,但上下滚动后出现数据丢失情况。初始加载时嵌套表格显示完整数据,滚动后嵌套表格内容消失。相关实现代码如下:

tb.delegate = self
tb.dataSource = self
tb.estimatedRowHeight = 150
tb.rowHeight = UITableView.automaticDimension

struct listesiparis {
        var adet : Int
        var fiyat : Double
        var urunadi : String
        var urunid : String
        var urunresim : String
        var urunadi_en: String
    }
    struct siparis {
        var docid : String
        var siparis : [listesiparis]
        var tarih : String
    }
    var siparisArray = [siparis]()

extension MusteriRapor: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableView == tb {
            return siparisArray.count
        } else {
            guard let selectedCell = tableView.superview?.superview?.superview as? MusteriRaporCell,
                  let selectedIndexPath = tb.indexPath(for: selectedCell) else {
                return 0
            }
            let selectedRow = selectedIndexPath.row
            return siparisArray[selectedRow].siparis.count
        }
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if tableView == tb {
            let cell = tb.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MusteriRaporCell
            let item = siparisArray[indexPath.row]
            cell.date.text = item.tarih

            cell.tbnestted.delegate = self
            cell.tbnestted.dataSource = self

            cell.siparisArray = item.siparis
            cell.tbnestted.reloadData()

            let nestedTableViewHeight = CGFloat(item.siparis.count * 70) + 24
            cell.cardconsttraint.constant = nestedTableViewHeight

            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MusteriRaporNestedCell
            
            guard let selectedCell = tableView.superview?.superview?.superview as? MusteriRaporCell,
                  let selectedIndexPath = tb.indexPath(for: selectedCell) else {
                return cell
            }
            let selectedRow = selectedIndexPath.row
            let selectedSiparis = siparisArray[selectedRow]
            
            let nestedItem = selectedSiparis.siparis
            
            cell.urunadi.text = nestedItem[indexPath.row].urunadi
            
            return cell
        }
    }
}

问题根源

  • 视图层级依赖不可靠:通过tableView.superview?.superview?.superview获取父单元格的方式完全依赖固定视图结构,Cell复用或布局调整时会直接失效,导致无法获取正确数据源。
  • Cell复用引发的数据源关联错误:父TableView的Cell被复用时,嵌套TableView的数据源未正确重新绑定,或父索引获取失败,最终显示空数据。
  • 自动高度计算冲突:父TableView使用automaticDimension但手动计算嵌套TableView高度的方式不严谨,可能导致Cell高度计算错误,影响嵌套TableView显示。

修复方案

1. 让嵌套Cell自行管理数据源

修改MusteriRaporCell,使其持有专属数据源并实现嵌套TableView的代理/数据源,彻底摆脱对外部视图层级的依赖:

class MusteriRaporCell: UITableViewCell {
    @IBOutlet weak var date: UILabel!
    @IBOutlet weak var tbnestted: UITableView!
    @IBOutlet weak var cardconsttraint: NSLayoutConstraint!
    
    // 持有当前Cell的嵌套数据源
    var siparisItems: [listesiparis] = [] {
        didSet {
            tbnestted.reloadData()
            // 重新计算嵌套TableView高度
            let nestedHeight = CGFloat(siparisItems.count * 70) + 24
            cardconsttraint.constant = nestedHeight
            layoutIfNeeded()
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // 初始化嵌套TableView的代理和数据源为自身
        tbnestted.delegate = self
        tbnestted.dataSource = self
        tbnestted.register(UINib(nibName: "MusteriRaporNestedCell", bundle: nil), forCellReuseIdentifier: "cell")
        // 禁用嵌套TableView滚动,避免与父TableView冲突
        tbnestted.isScrollEnabled = false
    }
}

// 实现嵌套TableView的代理和数据源方法
extension MusteriRaporCell: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return siparisItems.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MusteriRaporNestedCell
        let item = siparisItems[indexPath.row]
        cell.urunadi.text = item.urunadi
        return cell
    }
    
    // 设置固定行高,避免自动高度计算问题
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 70
    }
}

2. 简化父ViewController的数据源实现

移除对嵌套TableView代理的设置,仅负责给父Cell传递数据源:

extension MusteriRapor: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return siparisArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MusteriRaporCell
        let item = siparisArray[indexPath.row]
        cell.date.text = item.tarih
        // 直接将对应数据源传递给Cell
        cell.siparisItems = item.siparis
        return cell
    }
    
    // 确保父TableView自动高度正常工作
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
}

3. 额外优化建议

  • 移除父TableView的estimatedRowHeight或设置更准确的估计值,减少滚动时的高度跳动。
  • 确保嵌套TableView的行高为固定值,避免布局混乱。
  • 通过didSet监听数据源变化来更新嵌套TableView,替代在cellForRowAt中频繁调用reloadData(),提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:05:02