You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
}

额外优化建议

  1. 避免强制解包:我在代码里用了guard let来安全处理可选值,你原来的!强制解包很容易导致崩溃,一定要替换成安全的解包方式。
  2. 使用图片缓存库:原生URLSession下载图片没有缓存,而且Cell复用的时候容易出现图片错乱问题。用SDWebImage或者Kingfisher的话,一行代码就能搞定:cell.postPhoto.sd_setImage(with: imgUrl, placeholderImage: UIImage(named: "placeholder")),既省心又高效。

内容的提问来源于stack exchange,提问作者yufan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:27:52