#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
, но везде, где вы используете индекс 02. Измените это
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)