0

I have table view that delete some rows with following:

func deleteRows(_ indecies: [Int]) {
    guard !indecies.isEmpty else { return }
    let indexPathesToDelete: [IndexPath] = indecies.map{ IndexPath(row: $0, section: 0)}
    let previousIndex = IndexPath(row: indecies.first! - 1, section: 0)
    tableView.deleteRows(at: indexPathesToDelete, with: .none)
    tableView.reloadRows(at: [previousIndex], with: .none)
  }

In cellForRow i have cell that have "tap" closure like this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard indexPath.row < presenter.fields.count else { return EmptyCell() }
    let field = presenter.fields[indexPath.row]
    switch field.cellType {
    case .simple:
      guard let model = field as? SimpleTextItem else { return EmptyCell() }
      let cell = SimpleTextCell()
      cell.setup(label: LabelSL.regularSolidGray(), text: model.text, color: Theme.Color.bleakGray)
      return cell
    case .organization:
      guard let model = field as? OrganizationFilterItem else { return EmptyCell() }
      let cell = OrganizationFilterCell()
      cell.setup(titleText: model.title,
                 holdingNumberText: model.holdingNumberText,
                 isChosed: model.isChosed,
                 isHolding: model.isHolding,
                 isChild: model.isChild,
                 bottomLineVisible: model.shouldDrawBottomLine)

      cell.toggleControlTapped = {[weak self] in
        self?.presenter.tappedItem(indexPath.row)
      }
      return cell
    }
  }

When

cell.toggleControlTapped = {[weak self] in
            self?.presenter.tappedItem(indexPath.row)
          }

Tapped after rows deletion, index is pass is wrong (it's old). For example, i have 10 rows, i delete 2-3-4-5 row, and then i tap on 2 row (it was 6 before deletion). That method pass "6" instead of "2".

Problem actually was solved by adding tableView.reloadData() in deleteRows function, but, as you may assume smoothly animation gone and it look rough and not nice. Why is table still pass old index and how to fix it?

Evgeniy Kleban
  • 6,794
  • 13
  • 54
  • 107
  • DId you to try to add tableView.reloadData() ? on the last line of the function "deleteRows(_ indecies: [Int])" ? – Braham Youssef Jul 16 '19 at 11:05
  • 1
    The closure captures the index path when the cell is created, and that becomes invalid as soon as rows are inserted or deleted. See https://stackoverflow.com/q/28659845/1187415 for better methods to get the current index path of a cell. – Martin R Jul 16 '19 at 11:08

2 Answers2

1

A quite easy solution is to pass the cell in the closure to be able to get the actual index path

Delare it

var toggleControlTapped : ((UITableViewCell) -> Void)?

Call it

toggleControlTapped?(self)

Handle it

cell.toggleControlTapped = {[weak self] cell in
    guard let actualIndexPath = self?.tableView.indexPath(for: cell) else { return }
    self?.presenter.tappedItem(actualIndexPath.row)
}

Side note: Reuse cells. Creating cells with the default initializer is pretty bad practice.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • thanks for answer, what you mean with - Side note: Reuse cells. Creating cells with the default initializer is pretty bad practice. Can you provide example with "good" practice? – Evgeniy Kleban Jul 16 '19 at 11:29
  • 1
    The *good* practice is the standard practice: `let cell = tableView.dequeueReusableCell(withIdentifier: "TextCell", for: indexPath) as! SimpleTextCell`. `TextCell` is the identifier specified in Interface Builder. – vadian Jul 16 '19 at 11:32
  • @vadian, nice answer, it is interesting that If I use the default `indexPath` in func `cellForRowAt` & `reloadData()` instead of retrieving the indexPath with `tableView.indexPath(for: cell)`, this issue is not exist. But some of tableView of my app are using `deleteRows()`, then it will fail. It seems to me that tableView don't reload indexPath when you do `deleteRows`, so the next time delete will delete the previous indexPath stuff. Then we should fetch it manually. – Zhou Haibo Jun 29 '21 at 08:04
  • 1
    @ChuckZHB When cells are removed with `deleteRows(at:with:` then `cellForRowAt` is not called therefore the index path is not being updated. That's the reason to get the **actual** index path in the closure. – vadian Jun 29 '21 at 10:35
-2

Be sure to update your data too, indexes are appearing from numberofrows function of table view