如何在UITableViewCell中用UICollectionView展示场地列表图片?
我明白你遇到的问题了——在UITableViewCell嵌套UICollectionView的场景下,用全局变量venueLoaded传递数据很容易因为UITableView的cell复用机制,导致图片和对应Venue不匹配。下面是具体的解决思路和代码调整方案:
问题根源
你当前依赖全局的venueLoaded变量给CollectionView提供数据,但UITableView滚动时会复用cell,这个全局变量会被最新的Venue覆盖。之前的cell里的CollectionView在刷新或加载图片时,拿到的已经不是它对应的Venue数据了,自然会出现图片错乱。
解决方案:让每个UITableViewCell持有自己的数据源
我们需要把CollectionView的数据源逻辑内聚到对应的VenueListingCell中,让每个cell管理自己的CollectionView数据,彻底摆脱全局变量的依赖。
1. 重构VenueListingCell
修改你的VenueListingCell,添加venue属性并让它成为内部CollectionView的数据源/代理:
class VenueListingCell: UITableViewCell { // 给cell添加专属的venue数据源 var venue: Venue? { didSet { // 当venue更新时,自动刷新内部的CollectionView collectionView.reloadData() } } @IBOutlet weak var collectionView: UICollectionView! override func awakeFromNib() { super.awakeFromNib() // 设置CollectionView的数据源和代理为当前cell collectionView.dataSource = self collectionView.delegate = self // 注册CollectionView的cell(如果之前没在xib里注册的话) collectionView.register(UINib(nibName: "VenueListingCellImage", bundle: nil), forCellWithReuseIdentifier: "VenueListingCellImage") } } // 让VenueListingCell遵守CollectionView的协议 extension VenueListingCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // 返回当前venue的图片数量,为空时返回0 return venue?.imgURLs.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VenueListingCellImage", for: indexPath) as! VenueListingCellImage // 从当前cell持有的venue中获取图片URL,做安全校验 guard let currentVenue = venue, let imgURLString = currentVenue.imgURLs[indexPath.row], let imgURL = URL(string: imgURLString) else { // 处理空数据情况,比如显示占位图 cell.imageView.image = UIImage(named: "placeholder") return cell } cell.configureCell(url: imgURL) return cell } // 可选:如果需要设置CollectionView item的大小,实现这个方法 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height) } }
2. 修改TableView的cellForRowAt方法
删掉全局的venueLoaded变量,直接将对应Venue赋值给cell的venue属性:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "VenueListingCell", for: indexPath) as? VenueListingCell else { return UITableViewCell() } let venueCurrent = filteredVenueArray[indexPath.row] cell.venue = venueCurrent // 直接给cell的专属数据源赋值 cell.selectionStyle = .none return cell }
3. 清理旧代码
把原来在外部实现的collectionView(_:cellForItemAt:)方法删掉,现在这个逻辑已经由每个VenueListingCell自己处理了。
为什么这样能解决问题
每个VenueListingCell都持有自己对应的Venue对象,不管UITableView怎么复用cell,每个cell的CollectionView只会从自己持有的Venue中获取图片URL,完全避免了全局变量带来的数据混乱问题。同时,当cell被复用、赋值新的Venue时,didSet会自动刷新CollectionView,确保展示的是最新数据。
内容的提问来源于stack exchange,提问作者Roggie

