Index out of range after item has been deleted fro

2019-05-25 13:44发布

问题:

Please help me find what is my mistake. I have a table view the data source is an array called items. After one item has been deleted from the items array the cellForRowAt method called and the parameter indexPath.row is equals with items.count. This happens only, when the rows count is just enough to one item is out of the table view's view. When it happens it cause a fatal error (index out of range). A hack is used in this case and the value of the IndexPath.row be decreased.

Please see the following image:

The following code for cellForRowAt:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell

    var i = indexPath.row
    if i >= items.count { i = items.count - 1 } //XXX: hack! Sometimes called with indexPath.row is equal with items.count :(
    cell.set(items[i])

    return cell
}

And the following code for the delete:

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
    if (editingStyle == UITableViewCellEditingStyle.delete)
    {
        items.remove(at: indexPath.row)

        tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.fade)
    }
}

I think is not relevant, but I'm using iOS 11.0

UPDATE

I tried, and the following very simple code is also affected:

    class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
{
    var items = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]

    override func viewDidLoad()
    {
        super.viewDidLoad()
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath)
        cell.textLabel?.text = "\(items[indexPath.row])"

        return cell
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
    {
        if (editingStyle == UITableViewCellEditingStyle.delete)
        {
            items.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.fade)
        }
    }
}

How to reproduce:

  1. Scroll to down to Y be the last item on your screen

  1. Try to delete Y swiping to the left

  1. And getting the following error

回答1:

Your deletion code looks fine, but you should not do what you´re doing in your cellForRowAt. Do it like this instead:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
    cell.set(items[indexPath.row])

    return cell
}

And your numberOfRowsInSection should be like this:

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

Update:
We just confirmed that this was a bug related to Xcode 9 beta and iOS 11. Working fine on Xcode 8.x. OP will file a bug to Apple regarding this.