15

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.

BlueBoy
  • 798
  • 1
  • 11
  • 22
  • 1
    After removing the item in ur array... r u reloading the table..? if not reload the table ... it will solves ur issue – NAVEEN KUMAR Apr 29 '17 at 07:08
  • Tableview always returns correct index, can you show you delete index method i.e from where are you getting your "rowNum" – ankit Apr 29 '17 at 07:08
  • check the link : use nsnotification center http://stackoverflow.com/questions/36279894/how-to-reload-tableview-after-delete-item-in-cell-swift-2 – NAVEEN KUMAR Apr 29 '17 at 07:17
  • @ankit I have a button in my tableviewcell I am pressing, I set the index when in tableview.cellForRowAt – BlueBoy Apr 29 '17 at 18:14
  • Having the same issue here.. anyone found a fix? – Adrien Zier May 20 '17 at 15:10
  • 1
    How exactly is your code connected to the button action? Where and how do you define the button target? – Martin R May 22 '17 at 08:26
  • 2
    If you're deleting the cell using a custom button, where do you get `rowNum` from? – crizzis May 22 '17 at 21:47
  • Can you provide some screenshot of the cell and buttons? – Brian Nezhad May 23 '17 at 00:23
  • the tableview is not aware of the data source changes until you tell it to reloadData() which is why you see the problem go away when you call it. It's one line of code that you are trying to avoid by writing your own logic. – Chris May 23 '17 at 14:08
  • @Chris That's completely wrong. The table view know the data source has been updated because `deleteRows` is being called. You do not also need to call `reloadData`. – rmaddy May 23 '17 at 15:14
  • @BlueBoy You need to [edit] your question with clear details about where `rowNum` is coming from. My guess is you are using a tag. Never use tags to determine table row indexes. – rmaddy May 23 '17 at 15:16
  • See https://stackoverflow.com/questions/1802707/detecting-which-uibutton-was-pressed-in-a-uitableview and look at the Swift answer that doesn't use tags. – rmaddy May 23 '17 at 15:31
  • did you got your solution @AdrienZier\] – Dhiru May 26 '17 at 06:42
  • You can accept and thumbs up the solution which solved ur problem .....That can help other which have same problem ... @BlueBoy – Dhiru May 30 '17 at 02:59
  • @Dhiru none did. – BlueBoy May 31 '17 at 05:43
  • @crizzis rowNum is just the index of the cell being clicked on. I can then get the respective item from the array of data. – BlueBoy May 31 '17 at 05:44
  • use `sender.superview?.superview as? UITableviewCell` to get the actual cell as i have described in the answer below.... than get the `IndexPath` of that cell.... most important thing please keep in mind about View hierarchy .. i had two superView thats why wrote two times SuperView.superview` if you have more superview , than u have to write more times ,,,, check at once if this helped for u or not ?? @BlueBoy – Dhiru May 31 '17 at 06:51

11 Answers11

9

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 .

Community
  • 1
  • 1
Dhiru
  • 3,040
  • 3
  • 25
  • 69
2

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)
      }
    
vadian
  • 274,689
  • 30
  • 353
  • 361
  • That is what exactly what I am doing. But I get the bug which is the original question. That closure never updates unless you call reloadData(), which obviously reloads all the data. – BlueBoy Apr 30 '17 at 03:15
  • I tested the code. I does work. Maybe there is additional code which tampers the index path. – vadian Apr 30 '17 at 04:15
  • @vadian I done the same thing `postMedia.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .automatic)` .. on first I delete first row and on second row which should be first after delete have index path 1 and not 0 and its crashed obviously. Don't know why the table does not update its index path after row deleted. – Bhavin Bhadani Jul 12 '20 at 11:07
  • @vadian And does we also need to get the actual indexpath when select table cell as well after delete some cell? – Bhavin Bhadani Jul 12 '20 at 17:18
  • @EICaptainv2.0 If I understand your question correctly you get always the correct index path when `didSelectRow` is called – vadian Jul 12 '20 at 17:22
  • @vadian Right. Not just at didSelectRow but anywhere we used indexPath like on the buttons in table cell. – Bhavin Bhadani Jul 12 '20 at 17:23
  • @EICaptainv2.0 In the callback closure you must always retrieve the actual index path from the table view as described in the updated answer. – vadian Jul 12 '20 at 17:25
  • @vadian ok. So, simply whenever we need index path, we have to get it using cell – Bhavin Bhadani Jul 12 '20 at 17:30
2

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.

1

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.

Giuseppe Lanza
  • 3,519
  • 1
  • 18
  • 40
0

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)
    }
}
Vamshi Krishna
  • 979
  • 9
  • 19
  • I actually have a button in my tableviewcell which I want to press to delete the row. How can I do that with your code above? – BlueBoy Apr 29 '17 at 18:10
0

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];
Dimple Shah
  • 304
  • 3
  • 18
0

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)
Bala AP
  • 37
  • 5
0

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()

    }
}
Yellow
  • 175
  • 1
  • 15
0

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()
})
DrOverbuild
  • 1,245
  • 10
  • 11
-1

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.

gEeKyMiNd
  • 245
  • 1
  • 9
  • Is there a way to update the index values without the entire tableview being reloaded visually? Or is there some other way to do what I wanna do? – BlueBoy Apr 29 '17 at 18:28
  • you can try this http://stackoverflow.com/questions/35197766/how-to-delete-a-uitableview-row-on-a-button-action-in-swift for objective C this one - > http://stackoverflow.com/questions/29007807/delete-a-uitableview-row-by-click-on-custom-uibutton – gEeKyMiNd Apr 29 '17 at 19:40
-1

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()
Nick Marinov
  • 128
  • 1
  • 10
  • 2
    That is not correct, begin/endUpdates is needed only if you have to synchronize or animate *multiple* updates. – Martin R May 21 '17 at 14:25