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
相关产品推荐
相关产品推荐

