TableView嵌套CollectionView时获取选中Cell内容及响应方案
核心问题
现有代码存在3处逻辑错误,导致点击CollectionView单元格无法触发预期逻辑:
- 事件响应边界错误:
tableView(_:didSelectRowAt:)仅响应TableView自身单元格的点击,完全不会捕获内部嵌套CollectionView的单元格点击事件,两个视图的触摸事件互相独立。 - Cell获取方式错误:
dequeueReusableCell(withIdentifier:for:)的作用是创建/复用出队新的单元格,不是获取当前屏幕上已展示的指定位置单元格,用这个方法拿不到用户当前交互的cell实例上存储的selectedDeal值。 - 代理链路缺失:没有在TableViewCell内为嵌套的CollectionView设置正确的delegate、dataSource,点击事件根本没有传递到你写的
collectionView(_:didSelectItemAt:)实现中。
正确实现方案
第一步:在自定义TableViewCell中配置CollectionView,用闭包向外抛选中事件
不要在cell内部自行存储选中状态等待外部读取,直接通过闭包把选中的上下文信息传递给外层控制器,避免复用导致的数据错乱:
class SpecialPicTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource { @IBOutlet weak var collectionView: UICollectionView! // 标记当前cell对应TableView的行号,区分不同TableView行下的CollectionView var currentTableRow: Int! // 当前cell承载的数据源 var dealArray: [Deal] = [] // 选中回调:回传CollectionView选中的indexPath、对应数据、所属TableView行号 var onDealSelected: ((IndexPath, Deal, Int) -> Void)? override func awakeFromNib() { super.awakeFromNib() // 必须在cell内给CollectionView绑定代理、数据源 collectionView.delegate = self collectionView.dataSource = self collectionView.isUserInteractionEnabled = true } // MARK: CollectionView代理实现 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dealArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DealCollectionCell", for: indexPath) as! DealCollectionCell let deal = dealArray[indexPath.row] // 自行配置CollectionCell展示内容 return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedDeal = dealArray[indexPath.row] // 选中时直接触发回调,把数据抛给外层控制器 onDealSelected?(indexPath, selectedDeal, currentTableRow) } }
第二步:在ViewController的TableView数据源方法中绑定回调,处理跳转逻辑
不需要实现TableView的didSelectRowAt来处理CollectionView的点击,在cellForRowAt中给cell传值、绑定回调即可:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "SpecialPicTableViewCell", for: indexPath) as! SpecialPicTableViewCell // 给cell传递当前行号、对应数据源 cell.currentTableRow = indexPath.row cell.dealArray = // 传入当前TableView行对应的Deal数组 // 绑定选中回调 cell.onDealSelected = { [weak self] _, selectedDeal, _ in guard let self = self else { return } // 拿到选中数据直接处理跳转 self.dealSelected = selectedDeal.title ?? "" self.performSegue(withIdentifier: "goToDeal", sender: nil) } return cell }
注意事项
- 把原来
tableView(_:didSelectRowAt:)里处理CollectionView选中的代码全部删除,这个方法只有点击TableViewCell自身空白区域才会触发,点击内部CollectionView不会走到这里。 - 如果点击CollectionView完全无响应,检查视图层级是否有上层视图拦截触摸事件,确认CollectionView、它的父视图、TableViewCell的
isUserInteractionEnabled属性均为true。 - 如果确实需要获取屏幕上正在展示的指定位置cell,要用
tableView.cellForRow(at:)方法,不要用dequeueReusableCell,后者会生成新的cell实例,和当前用户看到的cell不是同一个对象。
内容的提问来源于stack exchange,提问作者WhySoSerious33
相关产品推荐
相关产品推荐

