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

