ios - UITableView 索引超出范围

标签 ios swift uitableview indexoutofrangeexception

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

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

有时我会收到此错误消息:

2017-12-09 14:19:11.431048+0100 Le Baluchon[19344:5162393] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (0) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)

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

Thread 1: Fatal error: Index out of range

有什么想法吗?

这是我的 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()
}
}
}

最佳答案

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

关于ios - UITableView 索引超出范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47729801/

相关文章:

ios - didBecomeActive 发布通知,我的 View Controller 从未听到它

Swift 数组设置 : Showing Recent Messages Command failed due to signal: Segmentation fault: 11

ios - 滚动时不会调用 estimatedHeightForRowAt

swift - 将多行的 heightForRowAt indexPath 设置为 0

ios - 标题部分 View UITableView 中的标签和按钮

iOS POST 到 Rails - 参数不包含 :modelname 的值

objective-c - 计算网页加载到 UIWebView 所需的时间

ios - 如何使用代码将 UISearchController Searchbar 放到导航栏上

swift - 应用蒙版后获得黑白图像

ios - iOS 7.1.2 的 UITableView 单元格高度