【问题标题】:Can't get the value from view model in the cell无法从单元格中的视图模型中获取值
【发布时间】:2022-01-09 22:41:29
【问题描述】:

我正在开发天气跟踪器,想在我的 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
}

我的 WeatherService,当我获取数据时:

protocol ITwentyFourHoursWeatherService {
    
    func getCitiesWeather(completion: @escaping (Result<TwentyFourHoursCitiesWeather, Error>) -> Void)
}

enum TwentyFourHoursWeatherServiceError: Error {
    case badUrl
}

private extension String {
    static let url = "https://api.openweathermap.org/data/2.5/forecast?q=London&cnt=3&appid=KEY"
}
    
final class TwentyFourHoursWeatherService: ITwentyFourHoursWeatherService {

    func getCitiesWeather(completion: @escaping (Result<TwentyFourHoursCitiesWeather, Error>) -> 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)") <--I get data here
            } catch {
                print("failed to convert \(error)")
            }
        }

        task.resume()
    }

}

我的 ViewModel:

类 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)") <-- Get data here
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
}

var twentyFourHoursWeatherDidChange: (() -> Void)?

我的视图控制器:

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)") <--And I get data here
            }
        }
        
        twentyFourHoursViewModel.twentyFourHoursViewDidLoad()
        
    }
    
//MARK: - Collection

extension MainScrenenViewController: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
            return 3
    
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> 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)") <-- 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")
    }
    
}

enter image description here

【问题讨论】:

  • 您将result.list[1].main.temp 分配给temp,但在其他任何地方您都使用索引0
  • 将此if let temp: String? = 更改为if let temp: String = 甚至if let temp =。 if let 返回一个可选项没有多大意义。此时您需要一个实际值。还可以尝试将那个长的可选链分解为离散的中间值,看看发生了什么。设置断点。使用非常强大的 Xcode 调试器来单步调试您的代码。请注意,当您首次显示集合视图时,您希望获得 nil,然后在重新加载时获得一个值
  • @Paulw11 写入if let temp = 时出现错误条件绑定的初始化程序必须具有可选类型,而不是“字符串”
  • @Paulw11 抱歉,我不明白这条评论:您将 result.list[1].main.temp 分配给 temp,但在其他任何地方都使用索引 0
  • 您在.success 的情况下说twentyFourHoursTemp: result.list[1].main.temp,但在您的日志中使用list[0]

标签: ios json swift xcode collectionview


【解决方案1】:

if let temp: String? = String(twentyFourHoursViewModel.twentyFourHoursWeather?.twentyFourHoursTemp ?? 1.1) 不正确,否则您至少应该使用 1.1 作为 temp。

而是尝试解开可选值并从那里使用它:

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

【讨论】:

  • 感谢您的回答!但我在这段代码中得到No exact matches in call to initializercellTwo.mainTemperatureLabel.text = String(weather.twentyFourHoursTemp)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-24
  • 1970-01-01
相关资源
最近更新 更多