UITableView嵌套横向UICollectionView传值typeSelected为空问题
问题原因
两个核心错误导致传值失效:
- 属性归属类错误:你在
tableView(_:cellForRowAt:)中给BTableViewCell实例赋值typeSelected,但该属性、以及依赖它的getDeals查询方法实际定义在内部的BCollectionViewCell类上。外层B类单元格本身不存在你赋值的这个属性,要么是编译时隐式生成了空值属性,要么是传值链路完全断裂,查询时读取到的永远是String()初始化的空字符串。 - 赋值后未触发查询:即便属性位置正确,仅给属性赋值不会自动执行查询逻辑,
getDeals没有调用入口自然不会拉取数据。再加上市Cell复用机制没有处理旧数据、旧请求残留,就算查到数据也可能出现滑动后内容错乱。
修复方案
1. 调整B类单元格的结构
把typeSelected属性、查询方法、结果存储数组都挪到外层BTableViewCell中,通过属性监听器实现赋值即触发查询刷新,不要把控制查询逻辑的属性放在内层CollectionCell上:
class BTableViewCell: UITableViewCell { // 连线内部横向滚动的UICollectionView @IBOutlet weak var innerCollectionView: UICollectionView! // 控制查询类型的属性,用didSet监听赋值动作 var typeSelected: String = "" { didSet { // 赋值后先清空旧数据,避免复用显示残留内容 dealArray.removeAll() innerCollectionView.reloadData() // 自动触发数据查询 getDeals { [weak self] in guard let self = self else { return } self.innerCollectionView.reloadData() } } } // 存储当前cell的查询结果 var dealArray: [PFObject] = [] override func awakeFromNib() { super.awakeFromNib() // 内部CollectionView的数据源、代理由当前B类Cell自己管理 innerCollectionView.dataSource = self innerCollectionView.delegate = self } override func prepareForReuse() { super.prepareForReuse() // 复用时清空旧数据、取消未完成请求,避免串数据 dealArray.removeAll() PFQuery.cancelAllRequests(forTarget: self) innerCollectionView.reloadData() } // 查询方法移到外层B类Cell中 func getDeals(_ completionHandler: @escaping () -> Void) { let query = PFQuery(className: "Deal") query.whereKey("DealType", equalTo: typeSelected) query.findObjectsInBackground(block: { [weak self] (objects: [PFObject]?, error: Error?) in guard let self = self, let resObjects = objects, error == nil else { completionHandler() return } self.dealArray = resObjects completionHandler() }) } } // MARK: 内部CollectionView数据源实现 extension BTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dealArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BCollectionCell", for: indexPath) as! BCollectionViewCell // 这里仅向内层Cell传单条展示数据,不要传查询类型参数 let currentDeal = dealArray[indexPath.item] // 配置Cell展示内容... return cell } }
2. 保留外层ViewController的传值逻辑
外层cellForRowAt的B类Cell配置逻辑不需要额外修改,去掉强制解包避免崩溃即可,属性监听器会自动触发后续查询刷新:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row.isMultiple(of: 2) { let cell = tableView.dequeueReusableCell(withIdentifier: "A", for: indexPath) as! ATableViewCell cell.typeLabel.text = dealTypeArray[indexPath.row].type cell.titleLabel.text = dealTypeArray[indexPath.row].title cell.selectionStyle = .none return cell } else { let cell = tableView.dequeueReusableCell(withIdentifier: "B", for: indexPath) as! BTableViewCell cell.selectionStyle = .none // 赋值后自动触发didSet逻辑,不需要手动调用查询 cell.typeSelected = dealTypeArray[indexPath.row].title ?? "" return cell } }
优化提示
- 不要随意对可选值使用强制解包
!,用??提供默认值,避免接口返回nil时触发崩溃 - 嵌套横向滚动列表的模式中,内层列表的数据源、代理由持有它的外层Cell自行管理,不要把逻辑全部抛给顶层ViewController,避免indexPath对应错乱
- 带异步网络请求的可复用Cell,必须在
prepareForReuse中清理旧状态、取消未完成请求,否则快速滑动列表时大概率出现内容串位
内容的提问来源于stack exchange,提问作者WhySoSerious33
相关产品推荐
相关产品推荐

