嵌套UITableView的UICollectionView数据显示异常问题求助
iOS嵌套UICollectionView数据匹配异常问题解决
问题描述
UITableView内部嵌套UICollectionView,使用包含7个section的data1数组作为数据源时,重新加载数据会出现图片与section名称不匹配的问题;但将数据源限制为4个section时,功能完全正常。
核心原因
问题源于UITableViewCell的复用机制:
- 当UITableView的cell被复用时,仅修改UICollectionView的
tag但未强制触发其重新加载数据,导致复用的cell仍显示之前section的内容。 CategoriesTableViewCell的awakeFromNib()仅在cell首次创建时执行,复用阶段不会重新同步数据源逻辑,进一步加剧数据错位。
解决方案
1. 给TableViewCell添加数据源绑定,替代全局依赖
避免直接依赖全局data1数组和collectionView.tag,改为给CategoriesTableViewCell传递对应section的数据源,确保数据与cell强绑定:
修改CategoriesTableViewCell:
class CategoriesTableViewCell: UITableViewCell { // Outlets @IBOutlet weak var collectionView: UICollectionView! //Constants let defaults = UserDefaults.standard // Variables var resetIndex: Bool = false var myCat: String = "" var didSelectItemAction: ((String) -> Void)? // 新增:当前section的数据源 var sectionData: quoteData? { didSet { // 数据源更新时自动刷新collectionView collectionView.reloadData() } } //Methods override func awakeFromNib() { super.awakeFromNib() collectionView.delegate = self collectionView.dataSource = self myCat = defaults.string(forKey: "DefaultQuotes")! resetIndex = defaults.bool(forKey: "ResetIndex") } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } }
在CategoriesViewController的tableView(_:cellForRowAt:)方法中传递数据源:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! CategoriesTableViewCell // 绑定当前section的数据源 cell.sectionData = data1[indexPath.section] cell.didSelectItemAction = { [weak self] selectedCat in guard let self = self else { return } self.existingCat = self.defaults.string(forKey: "DefaultQuotes") ?? "" self.defaultCat = selectedCat if !self.defaultCat.isEmpty { if self.defaultCat != "General" { self.adAlert() } else { self.defaults.set(self.defaultCat, forKey: "DefaultQuotes") self.navigationController?.popViewController(animated: true) } } else { self.navigationController?.popViewController(animated: true) } } return cell }
2. 修改CollectionView代理方法,使用绑定的数据源
不再依赖collectionView.tag和全局data1,直接用cell内的sectionData获取数据:
extension CategoriesTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let sectionData = sectionData, indexPath.row < sectionData.quotes.count else { return } let quotePath = sectionData.quotes[indexPath.row] let r = quotePath.index(quotePath.startIndex, offsetBy: 11)..<quotePath.index(quotePath.endIndex, offsetBy: -4) myCat = String(quotePath[r]) didSelectItemAction?(myCat) } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sectionData?.quotes.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! CategoriesCollectionViewCell let defaults = UserDefaults.standard let defQuote = defaults.string(forKey: "DefaultQuotes") guard let sectionData = sectionData, indexPath.row < sectionData.quotes.count else { return cell } let quotePath = sectionData.quotes[indexPath.row] cell.images.image = UIImage(named: quotePath) let r = quotePath.index(quotePath.startIndex, offsetBy: 11)..<quotePath.index(quotePath.endIndex, offsetBy: -4) let currentCat = String(quotePath[r]) let myLbl = cell.viewWithTag(80) as? UILabel myLbl?.text = currentCat let myIcon = cell.viewWithTag(83) as? UIImageView let padlock = cell.viewWithTag(81) as? UIImageView // 设置选中边框状态 if myLbl?.text == defQuote { cell.images?.layer.borderWidth = 2.5 cell.images?.layer.borderColor = UIColor.red.cgColor cell.images?.clipsToBounds = true } else { cell.images?.layer.borderWidth = 0.0 cell.images?.layer.borderColor = UIColor.clear.cgColor cell.images?.clipsToBounds = false } // 设置分类图标 let iconName = getImg(Inb: currentCat) if iconName.isEmpty { myIcon?.image = UIImage(named: "CatIcons/\(currentCat).png") } else { myIcon?.image = UIImage(systemName: iconName) } // 设置锁图标显示逻辑 if currentCat == "General" || sectionData.sectionType == "Free Today" { padlock?.isHidden = true } else { padlock?.isHidden = false } return cell } func getImg(Inb: String) -> String { switch Inb { case "General": return "rainbow" case "Hope": return "sunrise" case "Sleep": return "bed.double.circle" case "Mindfulness": return "brain.head.profile" case "Enjoy the Moment": return "figure.2" case "Calm": return "figure.yoga" default: return "" } } }
3. 修复didSelectItemAt的错误逻辑
原代码在didSelectItemAt中错误调用dequeueReusableCell,上述修改已移除该错误逻辑,直接通过数据源获取选中项信息。
为什么4个section时正常?
当section数量较少时,所有UITableViewCell都处于屏幕可视范围内,无需复用,因此每个CollectionView的tag和数据都能正确匹配;当section超过4个后,cell开始复用,旧数据未及时更新,导致显示错位。
内容的提问来源于stack exchange,提问作者Douglas W. Palme
相关产品推荐
相关产品推荐

