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

如何将ViewModel中的数据传递到UICollectionViewCell并在ViewController展示?

实现方案

你现有代码的核心逻辑已经正确,只需要修复解析字段不匹配、优化Cell赋值逻辑即可正常运行:

修复点1:修正JSON解析字段映射

OpenWeather接口返回的经纬度、最低最高温度都是下划线命名,需要给解析结构体加CodingKey映射,否则解析会失败:

// 修正Coordinate结构体
struct Coordinate: Decodable {
    var longitude: Double?
    var latitude: Double?
    
    enum CodingKeys: String, CodingKey {
        case longitude = "lon"
        case latitude = "lat"
    }
}

// 修正Main结构体
struct Main : Decodable {
    let temp, tempMin, tempMax : Double?
    let pressure, humidity : Int?
    
    enum CodingKeys: String, CodingKey {
        case temp
        case tempMin = "temp_min"
        case tempMax = "temp_max"
        case pressure, humidity
    }
}

修复点2:按封装规范调用Cell的配置方法

你已经在Cell中定义了configure方法,不需要直接修改Cell内部的Label属性,直接调用配置方法即可:

// 替换ViewController中cellForItemAt的实现
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sliderCell", for: indexPath) as! DayWeatherCell
    let temperature = main ?? 1.1
    cell.configure(with: DayWeatherCell.Model(mainTemperatureLabel: temperature))
    return cell
}

可选优化:保证ViewModel回调在主线程执行

避免后续UI操作漏切线程导致异常,在ViewModel触发回调时主动切换到主线程:

// 修改ViewModel中getData方法的解析成功逻辑
do {
    result = try JSONDecoder().decode(CitiesWeather.self, from: data)
    DispatchQueue.main.async {
        self.weatherDidChange?(result!)
    }
} catch {
    print("failed to convert \(error)")
}

原始代码参考

我的ViewModel

class DayWeatherViewModel {

    let url = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=APIKEY"
    
    func viewDidLoad() {
        getData(from: url)
    }
    
    func getData(from url: String) {
        
        guard let url = URL(string: url) else {
            print("Failed to parse URL")
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                print("something went wrong")
                return
            }
            
            var result: CitiesWeather?
            do {
                result = try JSONDecoder().decode(CitiesWeather.self, from: data)
                self.weatherDidChange?(result!)
            }
            catch {
                print("failed to convert \(error)")
            }
            
            guard let json = result else {
                return
            }
            print(json.coord?.latitude)
            print(json.coord?.longitude)
            print(json.weather)
            print(json.wind?.speed)
            
        }
        
        task.resume()
        
    }
    
    var weatherDidChange: ((CitiesWeather) -> Void)?
}

我的UICollectionViewCell

class DayWeatherCell: UICollectionViewCell, UIScrollViewDelegate {
    
    struct Model {
        let mainTemperatureLabel: Double
    }
    
    var mainTemperatureLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont(name: "Rubik-Medium", size: 36)
        label.text = "10"
        label.textColor = .white
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    func configure(with model: Model) {
        mainTemperatureLabel.text = String(model.mainTemperatureLabel)
    }
}

我的ViewController

class MainScrenenViewController: UIViewController {
    
    let viewModel: DayWeatherViewModel

    private var main: Double? {
       didSet {
          DispatchQueue.main.async {
              self.collectionView.reloadData()
          }
       }
    }

    var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(DayWeatherCell.self, forCellWithReuseIdentifier: "sliderCell")
        collectionView.layer.cornerRadius = 5
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = UIColor(red: 0.125, green: 0.306, blue: 0.78, alpha: 1)
        return collectionView
    }()

    init(viewModel: DayWeatherViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(collectionView)

        collectionView.dataSource = self
        collectionView.delegate = self
        
        setupConstraints()

        viewModel.weatherDidChange = { result in
            self.main = result.main?.temp
        }

        viewModel.viewDidLoad()

    }
}

extension MainScrenenViewController: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    } 
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sliderCell", for: indexPath) as! DayWeatherCell
        
        if let myd: String? = String(main ?? 1.1) {
            cell.mainTemperatureLabel.text = myd
        }
        
        return cell
    }
    
}

extension MainScrenenViewController: UICollectionViewDelegateFlowLayout {
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
    }
}

我的JSON解析结构体

struct CitiesWeather: Decodable {
    
    let coord : Coordinate?
    let cod, visibility, id : Int?
    let name : String?
    let base : String?
    let weather: [Weather]?
    let sys: Sys?
    let main: Main?
    let wind: Wind?
    let clouds: Clouds?
    let dt: Date?
    var timezone: Int?
}

struct Weather: Decodable {
    
    let id: Int?
    let main: MainEnum?
    let description: String?
    let icon: String?
}

struct Sys : Decodable {
    
    let type, id : Int?
    let sunrise, sunset : Date?
    let message : Double?
    let country : String?
}

struct Wind : Decodable {
    let speed : Double?
    let deg : Int?
}

struct Clouds: Decodable {
    let all: Int?
}

enum MainEnum: String, Decodable {
    case clear = "Clear"
    case clouds = "Clouds"
    case rain = "Rain"
}

内容的提问来源于stack exchange,提问作者Mikl.hart

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 17:27:02