致命错误:删除第二个单元格的索引超出范围

问题描述:

在我的自定义单元格中,我有一个计时器。当倒计数到达0时,我打电话给我的委托方法,单元格被自动删除。 问题是,当第二个单元格到达0时,我的应用程序崩溃,出现错误fatal error: Index out of range致命错误:删除第二个单元格的索引超出范围

在我的自定义单元格设置我的数据:

protocol MyDelegateName { 
func removeOfferExpired(offerId: String, indexPath: IndexPath) 
} 

class MyCustomCell: UITableViewCell { 
    var offer:Offers? 
    var cellIndexPath:IndexPath? 
    var delegate:MyDelegateName? 


    func setupData(offer:Offers, indexPath:IndexPath){ 
    self.offer = offer 
    self.cellIndexPath = indexPath 
    //...other code not relevant 
    } 

//When the time reach zero I call the following method 

func updateTime() { 
    if timeLeft > 0 { 
     timeLeft = endTime.timeIntervalSinceNow 
     offerExpiresLabel.textColor = UIColor.white 
     offerExpiresLabel.text = timeLeft.hmmss 
    }else { 
     offerExpiresLabel.textColor = UIColor.red 
     offerExpiresLabel.text = "Offer Expired" 
     timer.invalidate() 
     self.delegate?.removeOfferExpired(offerId: (self.offer?.offer_id!)!, indexPath: self.cellIndexPath!) 
    } 
} 

在我ViewController我安装我的手机里面的数据cellForRowAt:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let offer = offers[indexPath.row] 
    let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MyCustomCell 
    cell.setupData(offer: offer, indexPath: indexPath) 
    cell.delegate = self 
    return cell 
} 

内。然后func removeOfferExpired(offerId: String, indexPath: IndexPath)我曾尝试使用:

1. self.offers.remove(at: indexPath.row) 
    self.tableView.reloadData() 

2. self.offers.remove(at: indexPath.row) 
    self.tableView.deleteRows(at: [indexPath], with: .automatic) 
    self.tableView.reloadData() 

3. //and even try to "wrap" it inside begin/end updates 
    tableView.beginUpdates() 
    self.offers.remove(at: indexPath.row) 
    self.tableView.deleteRows(at: [indexPath], with: .automatic) 
    tableView.endUpdates() 

它总是第二次崩溃。我知道我在setupData中为单元格分配的索引路径在第一个单元格被删除后是不一样的,但我认为reloadData是更新其余单元格中的索引路径的方法。

+0

您是否忘记在单元重新加载时停止计时器? 您应该在单元格外移动定时器/删除逻辑,例如创建包含您的列表的模型对象,并将删除项目委托给控制器。 – vojer

+0

@vojer谢谢,但我不认为崩溃与计时器有任何关系。 – mat

+0

如果老计时器触发,代表可以接收来自单元的呼叫,该单元不再在屏幕上。 – vojer

您的主要问题是,您告诉单元格其索引路径,然后您的单元格将该索引路径传递给其委托。但是一个小区的索引路径并不稳定。它随着其他行的添加,删除或移动而改变。

您的细胞协议的方法应该将自身(细胞)作为参数传递,而不是索引路径。然后,委托可以查询表视图来查找单元的最新索引路径,并根据最新的索引路径执行行删除。

+0

明白了!我会尝试。当我滚动10-15提供并且单元格不可见时,您认为在单元格中使用计时器会出现问题吗?标签是否显示正确的倒计时? – mat

+1

只要您的自定义单元格正确地使'prepareForReuse'方法中的计时器失效,并在'setupData'方法中正确重置自身,您应该没问题。在单元中使用计时器似乎是正确的方法,至少是基于你发布的小代码。 – rmaddy

+0

谢谢!我会立即尝试。 – mat

正如rmaddy所说,我在做什么是完全错误的。这是我根据他的回答所做的:

func updateTime() { 
    if timeLeft > 0 { 
     timeLeft = endTime.timeIntervalSinceNow 
     offerExpiresLabel.textColor = UIColor.white 
     offerExpiresLabel.text = timeLeft.hmmss 
    }else { 
     offerExpiresLabel.textColor = UIColor.red 
     offerExpiresLabel.text = "Offer Expired" 
     timer.invalidate() 
     // when the time reach zero I passed self to the delegate instead of the indexPath 
     self.delegate?.removeOfferExpired(offerId: (self.offer?.offer_id!)!, cell: self as UITableViewCell) 
    } 
} 

protocol MyDelegateName { 
    func removeOfferExpired(offerId: String, cell: UITableViewCell) // delegate method now passes the cell instead of the index 
} 

func removeOfferExpired(offerId: String, cell: UITableViewCell) { 
    // and then I get the index path from the cell 
    let indexPath = tableView.indexPath(for: cell) 
    self.offers.remove(at: (indexPath?.row)!) 
    self.tableView.deleteRows(at: [indexPath!], with: .automatic) 
} 
+0

您对optionals的处理将会崩溃。在'removeOffsetExpired'方法中,你需要安全地处理'indexPath'为'nil'。在你的代码中'''的每一次使用都是等待发生的崩溃。 – rmaddy

+0

你说得对。我通常在打开包装之前检查是否有零。我会修好它。非常感谢。 – mat