Не удается получить значение из модели представления в ячейке

#ios #json #swift #xcode #collectionview

#iOS #json #быстрый #xcode #вид коллекции

Вопрос:

Я работаю над трекером погоды и хочу отобразить некоторые данные в своем ViewController, но я не понимаю, почему какое-то значение из моей модели представления находится на закрытии, но не отображается в моем представлении коллекции

JSON

 {  "cod": "200",  "message": 0,  "cnt": 3,  "list": [  {  "dt": 1638370800,  "main": {  "temp": 282.21,  "feels_like": 279.54,  "temp_min": 281.53,  "temp_max": 282.21,  "pressure": 998,  "sea_level": 998,  "grnd_level": 995,  "humidity": 71,  "temp_kf": 0.68  },  "weather": [  {  "id": 500,  "main": "Rain",  "description": "light rain",  "icon": "10d"  }  ],  "clouds": {  "all": 79  },  "wind": {  "speed": 5.05,  "deg": 288,  "gust": 10.52  },  "visibility": 10000,  "pop": 0.48,  "rain": {  "3h": 0.21  },  "sys": {  "pod": "d"  },  "dt_txt": "2021-12-01 15:00:00"  },  {  "dt": 1638381600,  "main": {  "temp": 280.71,  "feels_like": 277.76,  "temp_min": 279.79,  "temp_max": 280.71,  "pressure": 1000,  "sea_level": 1000,  "grnd_level": 998,  "humidity": 76,  "temp_kf": 0.92  },  "weather": [  {  "id": 500,  "main": "Rain",  "description": "light rain",  "icon": "10n"  }  ],  "clouds": {  "all": 87  },  "wind": {  "speed": 4.84,  "deg": 20,  "gust": 10.28  },  "visibility": 10000,  "pop": 0.88,  "rain": {  "3h": 1.15  },  "sys": {  "pod": "n"  },  "dt_txt": "2021-12-01 18:00:00"  },  {  "dt": 1638392400,  "main": {  "temp": 278.21,  "feels_like": 274.28,  "temp_min": 278.21,  "temp_max": 278.21,  "pressure": 1005,  "sea_level": 1005,  "grnd_level": 1002,  "humidity": 72,  "temp_kf": 0  },  "weather": [  {  "id": 803,  "main": "Clouds",  "description": "broken clouds",  "icon": "04n"  }  ],  "clouds": {  "all": 73  },  "wind": {  "speed": 5.62,  "deg": 345,  "gust": 10.72  },  "visibility": 10000,  "pop": 0.39,  "sys": {  "pod": "n"  },  "dt_txt": "2021-12-01 21:00:00"  }  ],  "city": {  "id": 2643743,  "name": "London",  "coord": {  "lat": 51.5085,  "lon": -0.1257  },  "country": "GB",  "population": 1000000,  "timezone": 0,  "sunrise": 1638344651,  "sunset": 1638374122  } }  

Моя структура

 struct TwentyFourHoursCitiesWeather: Decodable {  let cod: String  let message, cnt: Int  let list: [List]  let city: City }  // MARK: - City struct City: Decodable {  let id: Int  let name: String  let coord: Coord  let country: String  let population, timezone, sunrise, sunset: Int }  // MARK: - Coord struct Coord: Decodable {  let lat, lon: Double }  // MARK: - List struct List: Decodable {  let dt: Int  let main: TwentyFourHoursMain  let weather: [TwentyFourHoursWeather]  let clouds: TwentyFourHoursClouds  let wind: TwentyFourHoursWind  let visibility: Int  let pop: Double  let rain: Rain?  let sys: TwentyFourHoursSys  let dtTxt: String   enum CodingKeys: String, CodingKey {  case dt, main, weather, clouds, wind, visibility, pop, rain, sys  case dtTxt = "dt_txt"  } }  // MARK: - Clouds struct TwentyFourHoursClouds: Decodable {  let all: Int }  // MARK: - Main struct TwentyFourHoursMain: Decodable {  let temp, feelsLike, tempMin, tempMax: Double  let pressure, seaLevel, grndLevel, humidity: Int  let tempKf: Double   enum CodingKeys: String, CodingKey {  case temp  case feelsLike = "feels_like"  case tempMin = "temp_min"  case tempMax = "temp_max"  case pressure  case seaLevel = "sea_level"  case grndLevel = "grnd_level"  case humidity  case tempKf = "temp_kf"  } }  // MARK: - Rain struct Rain: Decodable {  let the3H: Double   enum CodingKeys: String, CodingKey {  case the3H = "3h"  } }  // MARK: - Sys struct TwentyFourHoursSys: Decodable {  let pod: String }  // MARK: - Weather struct TwentyFourHoursWeather: Decodable {  let id: Int  let main, weatherDescription, icon: String   enum CodingKeys: String, CodingKey {  case id, main  case weatherDescription = "description"  case icon  } }  // MARK: - Wind struct TwentyFourHoursWind: Decodable {  let speed: Double  let deg: Int  let gust: Double }  

My WeatherService, when I get data:

 protocol ITwentyFourHoursWeatherService {    func getCitiesWeather(completion: @escaping (Resultlt;TwentyFourHoursCitiesWeather, Errorgt;) -gt; Void) }  enum TwentyFourHoursWeatherServiceError: Error {  case badUrl }  private extension String {  static let url = "https://api.openweathermap.org/data/2.5/forecast?q=Londonamp;cnt=3amp;appid=KEY" }   final class TwentyFourHoursWeatherService: ITwentyFourHoursWeatherService {   func getCitiesWeather(completion: @escaping (Resultlt;TwentyFourHoursCitiesWeather, Errorgt;) -gt; Void) {  guard let url = URL(string: .url) else {  return completion(.failure(TwentyFourHoursWeatherServiceError.badUrl))  }   let task = URLSession.shared.dataTask(with: url) { data, response, error in  guard let data = data, error == nil else { return }   do {  let result = try JSONDecoder().decode(TwentyFourHoursCitiesWeather.self, from: data)  completion(.success(result))  print("24 weatherService: (result.list[0].main.temp)") lt;--I get data here  } catch {  print("failed to convert (error)")  }  }   task.resume()  }  }  

My ViewModel:

class TwentyFourHoursViewModel {

 // MARK: -Properties let twentyFourHoursWeatherService: ITwentyFourHoursWeatherService  var twentyFourHoursWeather: TwentyFourHoursMainScreenWeatherModel?  init(twentyFourHoursWeatherService: ITwentyFourHoursWeatherService) {  self.twentyFourHoursWeatherService = twentyFourHoursWeatherService }  func twentyFourHoursViewDidLoad() {    twentyFourHoursWeatherService.getCitiesWeather { [weak self] result in    switch result {  case .success(let result):  self?.twentyFourHoursWeather = .init(  twentyFourHoursTime: result.list[2].dtTxt ,  twentyFourHoursIcon: "sunset",  twentyFourHoursTemp: result.list[1].main.temp  )  self?.twentyFourHoursWeatherDidChange?()  print("In twentyFourHoursViewModel: (result.list[0].main.temp)") lt;-- Get data here  case .failure(let error):  print(error.localizedDescription)  }  } }  var twentyFourHoursWeatherDidChange: (() -gt; Void)?  

My ViewController:

 class MainScrenenViewController: UIViewController {    let twentyFourHoursViewModel: TwentyFourHoursViewModel    //CollectionView   var todayCollectionView: UICollectionView = {  let layout = UICollectionViewFlowLayout()  layout.scrollDirection = .horizontal  let todayCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)  todayCollectionView.register(TwentyFourHoursCollectionViewCell.self, forCellWithReuseIdentifier: "todayCell")  todayCollectionView.translatesAutoresizingMaskIntoConstraints = false  todayCollectionView.backgroundColor = .white  return todayCollectionView  }()    //MARK: - Initialization    init(twentyFourHoursViewModel: TwentyFourHoursViewModel) {  self.twentyFourHoursViewModel = twentyFourHoursViewModel  super.init(nibName: nil, bundle: nil)  }    required init?(coder: NSCoder) {  fatalError("init(coder:) has not been implemented")  }    //MARK: - Lifecycle    override func viewDidLoad() {  super.viewDidLoad()    view.backgroundColor = .white    view.addSubview(todayCollectionView)    todayCollectionView.dataSource = self  todayCollectionView.delegate = self    setupConstraints()    twentyFourHoursViewModel.twentyFourHoursWeatherDidChange = {  DispatchQueue.main.async {  self.collectionView.reloadData()  print("Значение из замыкания: (self.twentyFourHoursViewModel.twentyFourHoursWeather?.twentyFourHoursTemp)") lt;--And I get data here  }  }    twentyFourHoursViewModel.twentyFourHoursViewDidLoad()    }   //MARK: - Collection  extension MainScrenenViewController: UICollectionViewDataSource {    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -gt; Int {    return 3    }    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -gt; UICollectionViewCell {    let cellTwo = collectionView.dequeueReusableCell(withReuseIdentifier: "todayCell", for: indexPath) as! TwentyFourHoursCollectionViewCell    if let temp: String? = String(twentyFourHoursViewModel.twentyFourHoursWeather?.twentyFourHoursTemp ?? 1.1) {  cellTwo.mainTemperatureLabel.text = temp  print("Значение в ячейке: (temp)") lt;-- There is nil here  }    return cellTwo }  

Ячейка представления коллекции

 class TwentyFourHoursCollectionViewCell: UICollectionViewCell {    var mainTemperatureLabel: UILabel = {  let label = UILabel()  label.font = UIFont(name: "Rubik-Medium", size: 16)  label.textColor = .black  label.textAlignment = .center  label.translatesAutoresizingMaskIntoConstraints = false  return label  }()    override init(frame: CGRect) {  super.init(frame: frame)    contentView.addSubview(mainTemperatureLabel)    //self.contentView.layer.cornerRadius = 10    let constraints = [    mainTemperatureLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),  mainTemperatureLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),  mainTemperatureLabel.heightAnchor.constraint(equalToConstant: 30),  mainTemperatureLabel.widthAnchor.constraint(equalToConstant: 30),    ]  NSLayoutConstraint.activate(constraints)  }    required init?( coder: NSCoder) {  fatalError("init(coder:) has not been implemented")  }   }  

введите описание изображения здесь

Комментарии:

1. Вы присваиваете result.list[1].main.temp temp , но везде, где вы используете индекс 0

2. Измените это if let temp: String? = на if let temp: String = или даже if let temp = . Это не имеет особого смысла для if, если позволить вернуть необязательный. На данный момент вам нужна фактическая стоимость. Также попробуйте разбить эту длинную необязательную цепочку на дискретные промежуточные значения и посмотреть, что происходит. Установите точку останова. Используйте очень мощный отладчик Xcode для пошагового выполнения кода. Обратите внимание, что вы ожидали бы получить nil при первом отображении представления коллекции, а затем значение при его перезагрузке

3.@Paulw11 я получаю ошибку en, когда if let temp = инициализатор записи для условной привязки должен иметь необязательный тип, а не «Строка»

4. @Paulw11 извините, я не понимаю этого комментария: Вы присваиваете result.list[1].main.temp временному, но везде вы используете индекс 0

5. Вы говорите twentyFourHoursTemp: result.list[1].main.temp в своем .success случае, но в своих журналах вы используете list[0]

Ответ №1:

if let temp: String? = String(twentyFourHoursViewModel.twentyFourHoursWeather?.twentyFourHoursTemp ?? 1.1) это неверно, иначе у вас должно быть по крайней мере 1.1 в качестве временного.

вместо этого попробуйте развернуть необязательное значение и использовать его оттуда:

 if let weather = twentyFourHoursViewModel.twentyFourHoursWeather {  cellTwo.mainTemperatureLabel.text = String(weather.twentyFourHoursTemp)  print("Значение в ячейке: (weather.twentyFourHoursTemp)")  }  

Вы также инициализируете смешанный объект TwentyFourHoursWeather с результатом:

 self?.twentyFourHoursWeather = .init(   twentyFourHoursTime: result.list[2].dtTxt ,  twentyFourHoursIcon: "sunset",  twentyFourHoursTemp: result.list[1].main.temp  )  

Это требует времени от третьего объекта списка, жесткого кодирования значка и определения температуры от второго объекта списка. Остальная часть вашего кода, по-видимому, указывает на то, что вы хотели бы использовать первый объект для каждого, пока не произойдет изменение. т. е. result.list[0].dtTxt и т. д

Комментарии:

1. Спасибо вам за ваш ответ! Но я получаю No exact matches in call to initializer этот код: cellTwo.mainTemperatureLabel.text = String(weather.twentyFourHoursTemp)