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

Swift使用Alamofire下载图片赋值给CollectionView的ImageView的乱序与显示异常问题

问题根因定位

两个异常的核心触发逻辑完全来自代码逻辑错位:

  • 图片数组顺序错乱:异步网络请求完成顺序完全不可控,使用append向数组尾部追加图片的方式,会导致谁先下载完成谁排在前面,和接口返回的原始数据索引完全不匹配
  • 视图初始化后图片不显示,需滚动才展示:发起所有图片请求后立刻调用reloadData()时,图片下载任务还在执行,数组里根本没有对应图片数据,所有cell都被赋值为nil;后续图片陆续下载完成写入数组后,没有主动触发对应cell的刷新,只有用户滚动视图触发cell重新复用、重新执行cellForItemAt方法时,才会读到已经下载好的图片
  • 额外的隐藏崩溃风险:代码中多处使用强制解包,网络请求失败、图片数据损坏时会直接触发崩溃
针对性修复方案
  • 放弃动态append的图片存储方式,提前按照接口返回的游戏列表长度,初始化定长的可选图片数组,数组下标和游戏列表的索引一一对应,从结构上避免顺序错乱
  • 拆分刷新时机:接口返回游戏列表文本数据后,先刷新一次列表,把游戏名称这类即时可得的内容展示出来,图片位置先留占位状态
  • 单张图片下载完成后,仅刷新对应索引的cell,不需要等所有图片全部下载完成再统一刷新,保证图片加载完成后立刻展示
  • cell复用时主动重置图片状态,避免复用逻辑导致的图片错位
  • 移除所有强制解包逻辑,网络异常时做降级处理,避免崩溃
优化后可直接运行的代码
// 提前定义定长的可选图片数组,和游戏列表索引一一对应
private var gameImages = [UIImage?]()

func fetchData(){
    AF.request("https://api.rawg.io/api/games?key=ab904f882bf94fa9960857911498aa78", method: .get, parameters: nil, headers: nil, interceptor: nil)
        .validate(statusCode: 200 ..< 299)
        .responseData { [weak self] response in
            guard let self = self else { return }
            do {
                let game = try JSONDecoder().decode(Game.self, from: response.data!)
                self.games = game
                
                guard let gameResults = game.results else { return }
                // 初始化定长图片数组,长度和游戏列表完全一致
                self.gameImages = Array(repeating: nil, count: gameResults.count)
                
                // 先刷新一次列表,展示游戏名称文本
                DispatchQueue.main.async {
                    self.gamesCollectionView.reloadData()
                }
                
                // 逐个发起图片下载,携带原始索引
                for (index, gameItem) in gameResults.enumerated() {
                    guard let imageURL = gameItem.backgroundImage else { continue }
                    AF.request(imageURL, method: .get)
                        .validate(statusCode: 200 ..< 299)
                        .responseData { imageResponse in
                            guard let imageData = imageResponse.data,
                                  let image = UIImage(data: imageData) else { return }
                            // 把图片写入对应索引的位置,不做追加操作
                            self.gameImages[index] = image
                            // 仅刷新对应位置的cell
                            DispatchQueue.main.async {
                                let indexPath = IndexPath(item: index, section: 0)
                                self.gamesCollectionView.reloadItems(at: [indexPath])
                            }
                        }
                }
            } catch let jsonErr {
                print("接口解析错误: \(jsonErr)")
            }
        }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: GameCollectionViewCell.identifier, for: indexPath) as! GameCollectionViewCell
    cell.backgroundColor = .blue
    // 先重置cell状态,避免复用错位
    cell.imageView.image = nil
    cell.gameNameLabel.text = games?.results?[indexPath.item].name
    // 直接读取对应索引位置的图片,没有就留空
    cell.imageView.image = gameImages[indexPath.item]
    return cell
}

额外优化提示:生产环境中不建议直接在业务代码里写批量图片下载逻辑,可接入图片加载能力自动做内存缓存、磁盘缓存、cell复用时的请求取消,避免滚动时重复发起图片请求导致的流量浪费

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 05:06:01