致命错误:删除第二个单元格的索引超出范围
问题描述:
在我的自定义单元格中,我有一个计时器。当倒计数到达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
是更新其余单元格中的索引路径的方法。
答
您的主要问题是,您告诉单元格其索引路径,然后您的单元格将该索引路径传递给其委托。但是一个小区的索引路径并不稳定。它随着其他行的添加,删除或移动而改变。
您的细胞协议的方法应该将自身(细胞)作为参数传递,而不是索引路径。然后,委托可以查询表视图来查找单元的最新索引路径,并根据最新的索引路径执行行删除。
答
正如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)
}
您是否忘记在单元重新加载时停止计时器? 您应该在单元格外移动定时器/删除逻辑,例如创建包含您的列表的模型对象,并将删除项目委托给控制器。 – vojer
@vojer谢谢,但我不认为崩溃与计时器有任何关系。 – mat
如果老计时器触发,代表可以接收来自单元的呼叫,该单元不再在屏幕上。 – vojer