Swift:UITableView嵌套UICollectionView加载图片列表问题排查
解决UITableViewCell内嵌UICollectionView的数据源关联问题
你遇到的问题根源在于用全局变量i传递TableViewCell的索引——UITableView和UICollectionView都是复用机制,当TableView滚动时,i会被不断更新,而CollectionView的数据源方法调用时机往往滞后,导致取到完全错误的Post数据,自然没法正常显示图片列表。
给你一个标准的解决方案,核心思路是让每个HomeTableViewCell自己持有对应的Post对象,独立管理内部CollectionView的数据源:
第一步:改造HomeTableViewCell
把CollectionView的数据源代理移到Cell内部,给Cell添加一个post属性,当Post赋值时自动刷新CollectionView:
import UIKit class HomeTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { @IBOutlet weak var postsText: UILabel! @IBOutlet weak var collectionView: UICollectionView! // 让Cell持有当前对应的Post var post: Post? { didSet { // 赋值后更新文本并刷新CollectionView postsText.text = post?.postText collectionView.reloadData() } } override func awakeFromNib() { super.awakeFromNib() // 给CollectionView设置数据源和代理 collectionView.dataSource = self collectionView.delegate = self // 如果是纯代码创建Cell,记得注册ImageCell: // collectionView.register(PhotosCollectionCell.self, forCellWithReuseIdentifier: "ImageCell") } // MARK: UICollectionView 数据源方法 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // 直接从当前Cell的post里取图片数量,用空合运算符处理可选值避免崩溃 return post?.imageUrlList?.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! PhotosCollectionCell // 安全解包所有可选值,避免崩溃 guard let currentPost = post, let imageUrlString = currentPost.imageUrlList?[indexPath.row], let imgUrl = URL(string: imageUrlString) else { return cell } // 这里可以优化:建议用SDWebImage/Kingfisher这类图片缓存库,避免重复下载和Cell复用的图片错乱 URLSession.shared.dataTask(with: imgUrl) { [weak cell] data, response, error in if let error = error { print("图片下载错误:\(error)") return } guard let imageData = data, let image = UIImage(data: imageData) else { return } // 回到主线程更新UI,注意用weak cell避免循环引用 DispatchQueue.main.async { cell?.postPhoto.image = image } }.resume() return cell } // 可选:设置CollectionView的Item大小,比如每行3个正方形图片 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let itemWidth = (collectionView.bounds.width - 20) / 3 // 20是左右间距总和 return CGSize(width: itemWidth, height: itemWidth) } }
第二步:简化ViewController里的TableView代理方法
现在不需要全局变量了,直接把对应的Post赋值给Cell即可:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell", for: indexPath) as! HomeTableViewCell // 直接给Cell赋值对应的Post cell.post = postLists[indexPath.row] return cell }
额外优化建议
- 避免强制解包:我在代码里用了
guard let来安全处理可选值,你原来的!强制解包很容易导致崩溃,一定要替换成安全的解包方式。 - 使用图片缓存库:原生URLSession下载图片没有缓存,而且Cell复用的时候容易出现图片错乱问题。用SDWebImage或者Kingfisher的话,一行代码就能搞定:
cell.postPhoto.sd_setImage(with: imgUrl, placeholderImage: UIImage(named: "placeholder")),既省心又高效。
内容的提问来源于stack exchange,提问作者yufan
相关产品推荐
相关产品推荐

