BlueBoy
BlueBoy

Reputation: 856

delete row UITableView index issue

I'm using the code below to delete a row in my tableview. First I delete the object from my array and then from the tableview using this code:

let i = IndexPath(item: rowNum, section: 0)
myArray.remove(at: rowNum)
myTableView.deleteRows(at: [i], with: UITableViewRowAnimation.left)

However, if I delete another row right after, not the row I wanted to be deleted gets deleted. The issue is, even though I deleted the first item in the tableview (e.g. index 0), clicking on the new first row returns index 1... which is wrong, and deletes the second row. After deleting the first row the new row at the top should have an index of 0.

I can solve this problem by doing:

mTableView.reloadData()

but this seems wrong... I shouldn't have to reload all the data again.

What else can I do?

EDIT: I have a custom button in my tableviewcell I am pressing to delete the row - not swiping.

Upvotes: 15

Views: 6997

Answers (11)

vadian
vadian

Reputation: 285092

Smart solution to delete a row when a button is pressed in the corresponding cell.

  • In the custom cell class declare a callback variable

      var callback : ((UITableViewCell)->())?
    
  • In the custom cell class implement an IBAction and connect the button to that action

      @IBAction func buttonPressed(_ sender : UIButton) {
         callback?(self)
      }
    
  • In cellForRowAtIndexPath assign the closure containing the code to delete the cell

      cell.callback = { currentCell in
          let actualIndexPath = tableView.indexPath(for: currentCell)!
          self.myArray.remove(at: actualIndexPath.row)
          tableView.deleteRows(at: [actualIndexPath], with: .left)
      }
    

Upvotes: 2

DrOverbuild
DrOverbuild

Reputation: 1265

I was having this problem but none of the previous answers worked for me. If you run myTableView.reloadData() within the same block as myTableView.deleteRows(at:with:), the animation doesn't run correctly.

You can run myTableView.performBatchUpdates(updates:completion:) where you delete your row in the updates handler and reload your data in the completion handler. This way, the deletion animation runs properly and the index rows sync up.

myTableView.performBatchUpdates({
    myTableView.deleteRows(at: [i], with: UITableViewRowAnimation.left)
}, completion: {finished in
    myTableView.reloadData()
})

Upvotes: 1

Dhiru
Dhiru

Reputation: 3060

Problem

The problem in you case is the Tag you are setting on the Button , button Tag is not changed when you are deleting the Rows , as its the All Cell is not Reloaded. i guess you are setting tag in cellforRowIndexpath you try with putting tag on button in tableWillDisplayCell

or

the best way to do this is below .

Solution

you can get the IndexPath in other way

Suppose if your view hierarchy is

UITableViewCell-> contentView -> button

in you button click method

 if let cell = sender.superview?.superview as? UITableviewCell{
        let indexPath = myTableView.indexPath(for: cell)
    // Now delete the table cell
    myArray.remove(at: indexPath.row)
    myTableView.beginUpdates()
    myTableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.left)
    myTableView.endUpdates()
}

i hope this will work for you .

Upvotes: 9

Giuseppe Lanza
Giuseppe Lanza

Reputation: 3699

I'm not sure about from where you get your var rowNum, but to me this issue looks like something similar it already happened to me.

For me the problem was that passing to the cell the indexPath in the tableView: cellForRowAt indexPath: was causing this value to be not updated when a cell was deleted.

in other words, all the buttons that are ordered after the button of the deleted cell must have their index shifted by -1, or they will delete the row n+1.

row A index 0
row B index 1
row C index 2
row D index 3

deleting B it happens

row A index 0
row C index 2
row D index 3

while C should have index 1 and D should have index 2. In this case tapping the button to delete C will cause D to be deleted, and tapping the button to delete D will cause a crash.

I hope I was clear enough.

Upvotes: 1

Yellow
Yellow

Reputation: 175

I use the following code in my applications:

public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath){

    if editingStyle == UITableViewCellEditingStyle.delete
    {
        myArray.remove(at: indexPath.row)
        myArray2.remove(at: indexPath.row)
        // you would also save the new array here so that the next time you open the tableview viewController it shows the changes made.

        mytableview.reloadData()

    }
}

Upvotes: 0

Bala AP
Bala AP

Reputation: 37

After delete the row, reload particular row

let indexPath = IndexPath(item: rowNum, section: 0)
array.remove(at: rowNum)
tableView.beginUpdates()
myTableView.deleteRows(at: [i], with: UITableViewRowAnimation.automatic)
tableView.endUpdates()
self.tableView.reloadRows(at: [indexPath], with: .automatic)

Upvotes: 0

Dimple Shah
Dimple Shah

Reputation: 303

Use Apple's Code

The table view behaves the same way with reloading methods called inside an update block—the reload takes place with respect to the indexes of rows and sections before the animation block is executed. This behavior happens regardless of the ordering of the insertion, deletion, and reloading method calls.

[tv beginUpdates];
[tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[tv endUpdates];

Upvotes: 0

I think you store rowNum variable with your cell and it's set in cellForRow(at:) method. If what I think is right then here's a thing.

UITableView try to do at least work as possible. This means that after you delete a cell, UITableView won't gonna call cellForRow(at:) methods on its datasource again until you call reloadData() or reloadRows(at:animation:) methods.

So when you delete the first cell, rowNum variable on other cells won't gonna be updated until you call reloadData() as you tried and saw that's work.

My suggestion is don't keep rowNum variable at a cell but ask UITableView for the index path of any cell via indexPath(for:) method on UITableView instead.

Upvotes: 2

Nick Marinov
Nick Marinov

Reputation: 148

Any methods that insert or delete rows must be called inbetween tableView.beginUpdates() and tableView.endUpdates() . So your code must be:

let i = IndexPath(item: rowNum, section: 0)
myArray.remove(at: rowNum)

tableView.beginUpdates()

myTableView.deleteRows(at: [i], with: UITableViewRowAnimation.left)

tableView.endUpdates()

Upvotes: -1

gEeKyMiNd
gEeKyMiNd

Reputation: 245

If you want to delete tableview row from a custom button action except for tableview commit editingStyle delegate or any tableview delegate method, you have to call tableview reloadData to populate with updated index value.

Upvotes: -1

Vamshi Krishna
Vamshi Krishna

Reputation: 989

Try this!

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

        //1. remove data from model
        data.remove(at: indexPath.row)

        //2. remove row from view
        tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
    }
}

Upvotes: 0

Related Questions