UITableView索引超出范围

时间:2017-12-09 14:53:38

标签: ios swift uitableview indexoutofrangeexception

我的标签应用程序崩溃了,我真的无法找到解决方案。当应用程序第一次执行时,当我选择第三个选项卡项(天气预报)时,一切正常,表数据加载其中的所有数据,一切正常显示,用户可以毫无问题地进行交互。当我选择另一个标签项然后返回到天气预报项时,会发生崩溃问题。我担心的是,它有时会崩溃,但有时它不会......这是我真正无法理解的,为什么它连续几次都会工作一次,有时它会在第一次尝试时崩溃。我想在某些情况下在viewdidAppear加载时指向视图生命周期,我的数组没有及时填充,因此TableView正在尝试访问不存在和崩溃的数组元素。这是我尝试使用线程和dispatchQueue进行管理但仍然出现问题..

有什么想法吗?非常感谢你的帮助。

有时我收到此错误消息:

  

2017-12-09 14:19:11.431048 + 0100 Le Baluchon [19344:5162393] ***因未捕获的异常终止应用程序' NSInternalInconsistencyException',原因:'无效更新:无效第0节中的行数。更新后的现有部分中包含的行数(0)必须等于更新前该部分中包含的行数(4),加上或减去插入的行数或从该部分删除(插入0,删除0)并加上或减去移入或移出该部分的行数(0移入,0移出)

但很多时候,我在VC的第73行旁边收到此错误消息:

  

线程1:致命错误:索引超出范围

有什么想法吗?

这是我的VIewController:

class WeatherViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var weatherView: UITableView!
@IBOutlet weak var weatherDataLoading: UIActivityIndicatorView!

let error = Notification.Name(rawValue: "Error")
var selectedRow: Int?
var count: Int = 0
var names = [String]()
var temps = [String]()
var dates = [String]()
var climates = [String]()
var forecastDates = [String]()
var forecastClimates = [String]()

override func viewDidLoad() {
    super.viewDidLoad()

    self.weatherView.dataSource = self
    self.weatherView.delegate = self
    NotificationCenter.default.addObserver(self, selector: #selector(displayErrorAlert), name: error, object: nil)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    WeatherRequest.getWeatherCast(completion: { (names, temps, dates, climates, forecastdates, forecastclimates) in
        self.names = names
        self.temps = temps
        self.dates = dates
        self.climates = climates
        self.forecastDates = forecastdates
        self.forecastClimates = forecastclimates
        self.weatherView.reloadData()
        self.weatherDataLoading.isHidden = true
    })
}

@objc func displayErrorAlert() {

    let alertVC = UIAlertController(title: "Error", message: "Couldn't retrieve data from servor", preferredStyle: .alert)
    alertVC.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
    self.present(alertVC, animated: true, completion: nil)

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return names.count
}

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "table_cell", for: indexPath) as! WeatherViewCell

    cell.separatorInset = UIEdgeInsets.zero

    cell.firstStackView.constant = tableView.frame.height / 4
    cell.cityName.text = names[indexPath.item]
    cell.cityDate.text = dates[indexPath.item]
    cell.cityClimate.text = climates[indexPath.item]
    cell.cityTemp.text = temps[indexPath.item]

    cell.firstForecastedClimate.text = forecastClimates[count]
    cell.firstForecastedDate.text = forecastDates[count]
    count += 1
    cell.secondForecastedClimate.text = forecastClimates[count]
    cell.secondForecastedDate.text = forecastDates[count]
    count += 1
    cell.thirdForecastedClimate.text = forecastClimates[count]
    cell.thirdForecastedDate.text = forecastDates[count]
    cell.isExpanded = false

    count += 8

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    guard let cell = tableView.cellForRow(at: indexPath) as? WeatherViewCell
        else { return }

    selectedRow = indexPath.item

    cell.contentView.backgroundColor = UIColor.darkGray
    cell.contentView.alpha = 0.5

    cell.isExpanded = !cell.isExpanded

    self.weatherView.beginUpdates()
    self.weatherView.endUpdates()
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

    guard let cell = tableView.cellForRow(at: indexPath) as? WeatherViewCell
        else { return }

    self.weatherView.beginUpdates()
    cell.contentView.backgroundColor = nil
    cell.contentView.alpha = 1
    cell.isExpanded = false
    self.weatherView.endUpdates()
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    guard let selectedRow = selectedRow else { return }
    guard let cell = weatherView.cellForRow(at: [0,selectedRow]) as? WeatherViewCell
        else { return }
    cell.isExpanded = false
    weatherView.beginUpdates()
    cell.contentView.backgroundColor = nil
    cell.contentView.alpha = 1
    weatherView.endUpdates()
    clear()
}

func clear() {
    count = 0
    names = [String]()
    temps = [String]()
    dates = [String]()
    climates = [String]()
    forecastDates = [String]()
    forecastClimates = [String]()
}
}

这是我从API Yahoo Weather获取数据的类。

class WeatherRequest {

static func getWeatherCast(completion: @escaping ([String], [String], [String], [String], [String], [String]) -> ()) {

DispatchQueue.global(qos: .userInteractive).async {

var names = [String]()
var temps = [String]()
var dates = [String]()
var climates = [String]()

var forecastDates = [String]()
var forecastClimates = [String]()

    let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%20in(%22Miami%22%2C%20%22new%20york%22%2C%20%22chicago%22%2C%20%22philadelphia%22))%20and%20u%3D'c'&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")

    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in

        guard let myData = data else { return }

        do {
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            let root = try decoder.decode(Root.self, from: myData)
            for channel in root.channels {
                dates.append(channel.item.condition.date)
                temps.append(channel.item.condition.temp + " °C")
                climates.append(channel.item.condition.text)
            }

            for channel in root.channels {
                for forecast in channel.item.forecast {
                    forecastDates.append(forecast.date)
                    forecastClimates.append(forecast.text)
                }
            }
            names = ["Miami", "New York", "Chicago", "Philadelphia"]
            DispatchQueue.main.async {
            completion(names, temps, dates, climates, forecastDates, forecastClimates)
            }

        } catch let fetchError {
            print(fetchError)
        }
    }
    task.resume()
}
}
}

1 个答案:

答案 0 :(得分:0)

错误清楚地解释了它。调用开始更新和结束更新时,将像这样检查行数。更新后的行数应等于更新前的行数加上或减去添加或删除的行数。在您的情况下,api返回4个值,但是当您更改选项卡并返回时,计数更改为0(此处的api响应延迟)。因此,在更新4之前和更新0之后但不调用删除单元格。有关更多信息,请阅读beginupdates的说明,清楚记录。