【问题标题】:How to fix TableView numberOfRowsInSection returning nil?如何修复 TableView numberOfRowsInSection 返回零?
【发布时间】:2018-12-30 12:21:13
【问题描述】:

我正在尝试使用 OpenWeatherMap API 检索 5 天的预报,但我不知道为什么,但每次调用 weatherCount() 方法时它都会返回 nil。

在视图模型中,我使用打印语句来验证行数应为 40。我尝试使用保护语句并强制展开,这只会使程序崩溃。我尝试实现回调方法,但不认为我做对了。

WeatherViewModel

import Foundation
class WeatherViewModel {

    var weatherInfo: WeatherData?

    weak var delegate: WeatherDelegate?

    func getWeatherData() {
        let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
        guard let url = URL(string: weather) else {
            print("Could not reach the API endpoint") // this guard is not being hit
            return }

        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)

            DispatchQueue.main.async {
                guard error == nil else { // Checking for errors in the request
                    print("Error retrieved was: \(error)")
                    return
                }

                guard let weatherResponse = data else { //checks we got the data from request
                    print("Could not retrieve data instead got \(data)")
                    return }
            }

            do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .secondsSince1970

                let responseData = try decoder.decode(WeatherData.self, from: data!)
                DispatchQueue.main.async {
                    // print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
                    self.weatherInfo = responseData
                    print(self.weatherInfo)
                    print("Number of rows in section will be : \(self.weatherInfo?.list.count ?? 1)")
                }
            }
            catch let e as Error {
                print("Error creating current weather from JSON because: \(e.localizedDescription)")
                print("Error in parsing the JSON")
                NSLog("Error hit when calling weather service \(e)")
            }
        }
        task.resume()
    }
    func weatherCount() -> Int {
        let numberOfRows = self.weatherInfo?.list.count
        print("Number of rows in weatherCount : \(numberOfRows)")
        return numberOfRows ?? 1
    }
}

WeatherTableViewController

import UIKit
import Foundation

class WeatherTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var tableView: UITableView!

    lazy var viewModel: WeatherViewModel = {
        return WeatherViewModel()
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self
        self.tableView.dataSource = self

        DispatchQueue.main.async {
                self.viewModel.getWeatherData()
                self.tableView.reloadData()
            }
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 5
    }

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //print("Number of rows in section is: \(viewModel.weatherInfo?.list.count)")
        //print("Rows: \(viewModel.weatherCount())")
        return viewModel.weatherCount() ?? 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let weatherCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath)
        weatherCell.textLabel?.text = " The current temperature is : \(viewModel.weatherInfo?.list[indexPath.row].main?.temp ?? 0)"
        print(viewModel.weatherInfo?.list[indexPath.row].main?.temp)
        return weatherCell
    }
}

numberOfRowsInSection 应该返回 40 但返回 nil

天气

import Foundation

struct WeatherData: Decodable {
    let cod: String
    let message: Double
    let cnt: Int
    let list: [Info]
}

struct Info: Decodable {
    let dt: Date
    let main: WeatherInfo?

}

struct WeatherInfo: Decodable {
    let temp: Double
    let temp_min: Double
    let temp_max: Double
    let pressure: Double
    let sea_level: Double
    let grnd_level: Double
    let humidity: Int
    let temp_kf: Double
}

private enum CodingKeys: String, CodingKey {

    case minTemp = "temp_min"
    case maxTemp = "temp_max"
    case seaLevel = "sea_level"
    case temp
    case pressure
    case groundLevel = "grnd_level"
    case humidity
    case temp_kf
}

【问题讨论】:

  • 欢迎来到 StackOverflow!考虑到weatherCount() 调用weatherInfo?.list.count,我的猜测是关于WeatherData 的设置方式,或者数据如何映射到它。您能否添加WeatherData 进行澄清?
  • 当然是刚刚添加的,这里还是不知道怎么格式化抱歉!

标签: ios swift


【解决方案1】:

使用completion 处理程序从天气数据解析中获取通知,然后重新加载tableView,如下所示,

func getWeatherData(_ completion: @escaping () -> Void) {
       let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
        guard let url = URL(string: weather) else {
            print("Could not reach the API endpoint") // this guard is not being hit
            return }

        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)

                guard error == nil else { // Checking for errors in the request
                    print("Error retrieved was: \(error)")
                    return
                }

                guard let weatherResponse = data else { //checks we got the data from request
                    print("Could not retrieve data instead got \(data)")
                    return 
                }

            do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .secondsSince1970

                let responseData = try decoder.decode(WeatherData.self, from: data!)
                DispatchQueue.main.async {
                    // print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
                    self.weatherInfo = responseData
                    completion()
                }
            }
            catch let e as Error {
                print("Error creating current weather from JSON because: \(e.localizedDescription)")
                print("Error in parsing the JSON")
                NSLog("Error hit when calling weather service \(e)")
            }
        }
        task.resume()
}

更新viewDidLoad为,

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.delegate = self
    self.tableView.dataSource = self

    self.viewModel.getWeatherData() {
        self.tableView.reloadData()
    }
}

【讨论】:

  • 谢谢它的工作! ,完成处理程序和回调方法是一回事吗?我尝试创建一个委托,例如协议 WeatherDelegate: class { func didRecieve(forecast: WeatherData) func setRowsInSection(rows: Int) } 我认为这与完成处理程序相同,我的理解是否错误,因为我的方法不起作用?
  • 是的。实现相同目标的不同方法。 protocol 是更复杂的委派方式。它更适合维护和快速增强。对于简单的用例,completionHandler 很好。它不起作用的原因可能是您忘记在ViewControllerViewModel 之间正确挂钩。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多