嵌套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
相关产品推荐
相关产品推荐

