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

