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

Swift 4中存储字典数组并在Collection View展示及JSON解析问题

嘿,我来帮你搞定Swift 4里把接口拿到的字典数组转成结构体数组,再在Collection View里展示这件事~

步骤1:完善数据解析(优化结构体+正确解析JSON)

首先,你的自定义结构体可以用Swift 4引入的Codable协议来简化解析,比手动逐个取字典字段更靠谱。先重构结构体:

// 定义单个卡车数据的结构体,遵循Codable
struct Truck: Codable {
    let truckName: String
    let truckCost: [String]
}

// 如果接口返回的是一个包含卡车数组的JSON对象(比如{"trucks": [...]}),再定义一个对应整个响应的结构体
struct TruckResponse: Codable {
    let trucks: [Truck]
}

// 存储解析后的数据数组
var trucksArray = [Truck]()

接下来用Alamofire请求并解析数据,这里有两种方式:

方式一:用Codable自动解析(推荐)

Alamofire支持直接解析到Codable类型,代码更简洁:

func fetchTrucksData() {
    let param: [String: Any] = ["你的参数键": "参数值"]
    let header: HTTPHeaders = ["Content-Type": "application/json"]
    
    Alamofire.request("你的接口URL", method: .post, parameters: param, encoding: URLEncoding.default, headers: header)
        .responseDecodable(of: TruckResponse.self) { response in
            switch response.result {
            case .success(let truckResponse):
                // 拿到解析后的数组
                self.trucksArray = truckResponse.trucks
                // 必须在主线程刷新Collection View(Alamofire的回调默认在主线程,所以直接调用即可)
                self.collectionView.reloadData()
            case .failure(let error):
                print("请求/解析失败:\(error.localizedDescription)")
            }
    }
}

方式二:手动解析字典数组(适合老版本或特殊格式)

如果不想用Codable,也可以手动遍历字典数组转成结构体:

func fetchTrucksData() {
    let param: [String: Any] = ["你的参数键": "参数值"]
    let header: HTTPHeaders = ["Content-Type": "application/json"]
    
    Alamofire.request("你的接口URL", method: .post, parameters: param, encoding: URLEncoding.default, headers: header)
        .responseJSON { response in
            switch response.result {
            case .success(let value):
                // 假设接口返回的JSON是字典,里面的"trucks"字段是我们需要的数组
                guard let jsonDict = value as? [String: Any],
                      let trucksJSONArray = jsonDict["trucks"] as? [[String: Any]] else {
                    print("JSON格式不符合预期")
                    return
                }
                // 遍历字典数组,转成Truck结构体
                self.trucksArray = trucksJSONArray.compactMap { dict in
                    guard let name = dict["truckName"] as? String,
                          let cost = dict["truckCost"] as? [String] else {
                        return nil // 字段缺失就跳过这个数据
                    }
                    return Truck(truckName: name, truckCost: cost)
                }
                // 刷新Collection View
                self.collectionView.reloadData()
            case .failure(let error):
                print("请求失败:\(error.localizedDescription)")
            }
    }
}
步骤2:配置Collection View的数据源与代理

首先让你的ViewController遵守UICollectionViewDataSource和UICollectionViewDelegateFlowLayout协议,然后实现必要的方法:

class YourViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    @IBOutlet weak var collectionView: UICollectionView!
    var trucksArray = [Truck]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 注册自定义cell(如果是用XIB创建的cell)
        collectionView.register(UINib(nibName: "TruckCell", bundle: nil), forCellWithReuseIdentifier: "TruckCell")
        
        // 设置数据源和代理
        collectionView.dataSource = self
        collectionView.delegate = self
        
        // 发起网络请求获取数据
        fetchTrucksData()
    }
    
    // MARK: - UICollectionView 数据源方法
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 返回数组的元素个数
        return trucksArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // 取出复用的cell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TruckCell", for: indexPath) as! TruckCell
        
        // 拿到当前位置的Truck数据
        let currentTruck = trucksArray[indexPath.item]
        
        // 给cell的UI控件赋值
        cell.nameLabel.text = currentTruck.truckName
        // 把价格数组转成字符串展示,比如用" | "分隔
        cell.costLabel.text = "价格选项:\(currentTruck.truckCost.joined(separator: " | "))"
        
        return cell
    }
    
    // MARK: - UICollectionView 布局代理(可选,自定义cell大小)
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // 设置cell宽度为屏幕宽度减20(左右各留10间距),高度100
        return CGSize(width: view.bounds.width - 20, height: 100)
    }
    
    // 这里放上面的fetchTrucksData方法
}
步骤3:自定义CollectionViewCell示例

假设你创建了一个名为TruckCell的XIB文件,里面有两个UILabel用于展示卡车名称和价格:

class TruckCell: UICollectionViewCell {
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var costLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // 给cell加一点样式,比如圆角和边框
        layer.cornerRadius = 8
        layer.borderWidth = 1
        layer.borderColor = UIColor.lightGray.cgColor
        // 给label设置字体样式
        nameLabel.font = UIFont.boldSystemFont(ofSize: 16)
        costLabel.font = UIFont.systemFont(ofSize: 14)
        costLabel.textColor = .gray
    }
}
一些注意事项
  • 确保接口返回的JSON字段名和结构体的属性名完全一致,如果字段名不一样(比如接口里是truck_name),可以用CodingKey自定义映射:
    struct Truck: Codable {
        let truckName: String
        let truckCost: [String]
        
        enum CodingKeys: String, CodingKey {
            case truckName = "truck_name"
            case truckCost = "truck_cost"
        }
    }
    
  • 网络请求默认在后台线程,但Alamofire的response回调是在主线程执行的,所以直接调用collectionView.reloadData()是安全的。如果手动切换线程了,一定要回到主线程刷新UI。
  • 如果是用Storyboard创建的Collection View Cell,记得在Storyboard里给cell设置正确的Reuse Identifier,就不用在代码里注册XIB了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:54:42