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

Swift中用Alamofire加载JSON到CollectionView遇空行+Kingfisher图片加载问题

问题排查与解决方案

一、CollectionView空单元格问题排查

针对你的代码,从以下几个方向逐一排查:

1. 修正ViewModel初始化顺序

原ViewModel中先调用loadFoods()再绑定Repo的foodList,会导致ViewController订阅的是初始空的BehaviorSubject,而非Repo中实际接收数据的实例。调整顺序如下:

class HomeViewModel {
    var foodList: BehaviorSubject<[Foods]>!
    var frepo = FoodsDaoRepository()

    init() {
        // 先绑定Repo的Subject,再触发数据加载
        foodList = frepo.foodList
        loadFoods()
    }

    func loadFoods() {
        frepo.loadFoods()
    }
}

2. 确认JSON解码匹配度

  • 确保ProductsResponse结构与接口返回的JSON格式完全一致,例如接口返回{"foods": [...]},则结构体需定义为:
    struct ProductsResponse: Codable {
        let foods: [Foods]?
    }
    
  • 检查Foods类的属性名与JSON字段是否一一对应,若字段名不匹配,需添加CodingKeys映射:
    class Foods: Codable {
        var id: Int?
        var name: String?
        var image: String?
        var price: Int?
        var category: String?
    
        enum CodingKeys: String, CodingKey {
            case id = "food_id" // 替换为实际接口字段名
            case name = "food_name"
            case image = "food_image"
            case price = "food_price"
            case category = "food_category"
        }
    }
    

3. 增加网络请求日志排查

在Repo的loadFoods方法中添加详细日志,定位是网络请求失败还是解码错误:

func loadFoods() {
    AF.request("http://example.com/foods/getAllFoods.php", method: .get).response { response in
        switch response.result {
        case .success(let data):
            do {
                let result = try JSONDecoder().decode(ProductsResponse.self, from: data)
                if let list = result.foods {
                    print("成功加载\(list.count)条数据")
                    self.foodList.onNext(list)
                } else {
                    print("接口返回foods为空")
                    self.foodList.onNext([])
                }
            } catch {
                print("JSON解码错误:\(error)")
            }
        case .failure(let error):
            print("网络请求失败:\(error.localizedDescription)")
        }
    }
}

4. 检查CollectionView数据源实现

确保ViewController中全局变量foodsList正确赋值,且数据源方法返回正确数量:

var foodsList: [Foods] = []

// 数据源方法示例
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return foodsList.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FoodCell", for: indexPath) as! FoodCell
    let food = foodsList[indexPath.item]
    cell.foodNameLabel.text = food.name
    return cell
}

二、Kingfisher结合Alamofire加载图片

Kingfisher支持自定义网络请求器,可配置为使用Alamofire作为底层网络库:

1. 自定义Alamofire请求器

创建遵循ImageDownloadRequestor的类:

import Kingfisher
import Alamofire

class AlamofireImageRequestor: ImageDownloadRequestor {
    func downloadImage(with url: URL, options: KingfisherParsedOptionsInfo, completionHandler: @escaping (Result<ImageDownloadResult, KingfisherError>) -> Void) -> DownloadTask? {
        let request = AF.request(url, method: .get)
        let task = request.downloadProgress { progress in
            // 可选:监听下载进度
            print("图片下载进度:\(progress.fractionCompleted)")
        }.responseData { response in
            switch response.result {
            case .success(let data):
                let result = ImageDownloadResult(originalURL: url, url: url, data: data, mimeType: response.response?.mimeType)
                completionHandler(.success(result))
            case .failure(let error):
                let kingfisherError = KingfisherError.requestError(reason: .networkError(error))
                completionHandler(.failure(kingfisherError))
            }
        }
        return task
    }
}

2. 配置并使用

  • 全局配置(可选,应用内所有Kingfisher请求都使用Alamofire):
    KingfisherManager.shared.defaultOptions += [.requestor(AlamofireImageRequestor())]
    
  • 单个ImageView加载:
    if let imageUrl = URL(string: food.image ?? "") {
        cell.foodImageView.kf.setImage(
            with: imageUrl,
            options: [.requestor(AlamofireImageRequestor())]
        )
    }
    

简化用法(可选)

若无需自定义请求逻辑,直接使用Kingfisher默认加载即可,它已内置缓存、异步处理等功能:

if let imageUrl = URL(string: food.image ?? "") {
    cell.foodImageView.kf.setImage(with: imageUrl)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:10:33