NXException when removing and then adding an itme to UITableView - ios

When I remove an object from a list and try to add a new one in I get this error
2020-01-24 14:40:26.692343+1300 HappyDays[25416:1017241] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I see other issues similar to this but I've tried implementing those solutions . (Similar issue , another similar issue)
Sections of my code:
Selecting the cell to delete if from the table
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
activeObjectives.actives.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .left)
}
Adding a new object to the table by tapping a button
#IBAction func getGoals(_ sender: UIButton) {
let newRowIndex = activeObjectives.actives.count
activeObjectives.setRandomGoalToActive(allObjectiveList: allGoalsArray)
addActiveGoalToactiveObjectives()
let indexPath = IndexPath(row: newRowIndex, section: 0)
let indexPaths = [indexPath]
activeGoalDisplay.insertRows(at: indexPaths, with: .automatic)
}
In case it's also needed, here is my addActiveGoalToactiveObjectives function called in the above
func addActiveGoalToactiveObjectives() {
for goal in allGoalsArray.goalsList {
if goal.isActive == true && activeObjectives.actives.contains(goal) == false {
activeObjectives.actives.append(goal)
}
}
}
Thanks in advance, any help is greatly appreciated

I found the issue. I wasn't setting an object value to false when removing it from the list. Now when I remove them I set the object to inactive.
activeObjectives.actives[indexPath.row].setInactive()
activeObjectives.actives.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .left)
}```

Related

Invalid update: invalid number of items in section 0

I am hitting the following error when attempting to drag items from one UICollectionView (collectionView) into another (rackView):
'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (7) must be equal to the number of items contained in that section before the update (6), plus or minus the number of items inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).'
The strange thing is, almost the exact same code works for dragging and dropping when I am dragging the items from rackView into collectionView. rackView updates correctly when items are deleted from rack datasource but crashes when items are added back into it from the board (collectionView). Does anyone know why this is happening and is there any way to solve it?
Thanks for any information you can provide.
I've tried to add a variable which represents the count that's in the rack and return that in the numberOfItemsInSection method. This is stated as a possible solution for this error elsewhere but doesn't seem to resolve it for me.
private func moveItemsFromRack(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView)
{
collectionView.performBatchUpdates({
for (index, item) in coordinator.items.enumerated()
{
let indexPath = IndexPath(row: destinationIndexPath.row + index, section: destinationIndexPath.section)
self.board[indexPath.row] = item.dragItem.localObject as! String
self.rack.remove(at: self.sourceIndex.row)
}
DispatchQueue.main.async {
collectionView.reloadItems(at: [destinationIndexPath])
self.rackView.reloadData()
}
})
self.sourceIndex = []
}
private func moveItemsFromBoard(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView)
{
collectionView.performBatchUpdates({
for (index, item) in coordinator.items.enumerated()
{
let indexPath = IndexPath(row: destinationIndexPath.row + index, section: destinationIndexPath.section)
self.rack.insert(item.dragItem.localObject as! String, at: indexPath.row)
self.board[self.sourceIndex.row] = ""
}
DispatchQueue.main.async {
collectionView.reloadItems(at: [self.sourceIndex])
self.rackView.reloadData()
}
})
self.sourceIndex = []
}
I actually just removed the performBatchUpdates method and this issue was resolved.
An explanation for why is here:
https://fangpenlin.com/posts/2016/04/29/uicollectionview-invalid-number-of-items-crash-issue/

How to remove data from array on click of close button

I have an array I need to add and remove cell data on click of "close" which is UIButton in header section
I am looping the array and getting row and for section I have used static data and appending in indexPaths then after
tblSideNav.deleteRows(at: indexPaths, with: .fade)
as above line start executing app got crash
var menusArray = [sideNavStruct(isOpend: true, menuImg: "user", menuTitle: "Profile", subMenu: ["My Profile", "Distributor Profile"]), sideNavStruct(isOpend: true, menuImg: "user", menuTitle: "Reports", subMenu: ["Stock Report", "NFR Report", "RTD”])]
#objc func handleOpenCloseCell() {
let section = 0
var indexPaths = [IndexPath]()
for row in menusArray[section].subMenu.indices {
let indexPath = IndexPath(row: row, section: section)
indexPaths.append(indexPath)
}
print(indexPaths.count)
print(menusArray[section].subMenu[0])
menusArray[section].subMenu[0].removeAll()
tblSideNav.deleteRows(at: indexPaths, with: .fade)
}
my app got crash on tblSideNav.deleteRows(at: indexPaths, with: .fade):
Invalid update: invalid number of rows in section 0. The number of
rows contained in an existing section after the update (7) must be
equal to the number of rows contained in that section before the
update (2), plus or minus the number of rows inserted or deleted from
that section (0 inserted, 2 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out).
I am not able to find the issue
First of all, you are calling deleteRows without beginUpdates and endUpdates which is required.
tableView.beginUpdates()
tableView.deleteRows(at: indexPaths, with: .fade)
tableView.endUpdates()
The same error can occur if your delegate function is not returning the correct number of rows after the update.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowCount
}
I believe that better practice would be to change/empty the variable where you're storing your rows information/data and simply call reloadData().

iOS app crashes after deleting the last cell in section

So I've my app crashing everytime I try to delete the last cell of the section.
Ex., if my section has 10 rows, I can delete them without any problem but the last one throws the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
I've searched around here and found some ways to fix this problem, but I tried them all and couldn't get this issue fixed, it still crashes and throws the same error.
The related parts of my code goes as it follows:
override func numberOfSections(in tableView: UITableView) -> Int {
if (main_arr.count > 0 && sub_arr.count > 0) {
self.numberOfSections = 3
} else {
self.numberOfSections = 2
}
return self.numberOfSections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == 0) {
return 1
} else if (section == 1 && main_arr.count > 0) {
return main_arr.count
} else {
return sub_arr.count
}
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
switch indexPath.section {
case 1:
main_arr.remove(at: indexPath.row)
case 2:
sub_arr.remove(at: indexPath.row)
default:
print("default 001")
}
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
EDIT 1
I tried to handle the global variable, responsible for the numberOfSections so when any of my arrays.count == 0 it will be decreased by 1, but it didn't solved the issue.
I do understand completely the error message, like, if I've 3 sections, and I delete the whole content of one of them, I should delete one section and delete it from the datasource as well.
The problem is that numberOfSections returns different values before and after you delete rows, but you don't delete any sections. So you should either return a constant value in numberOfSections or call deleteSections in addition to deleteRows
The main thing to remember is the following:
UITableView must always contain the same number of rows and sections as your dataSource.
You cannot just return a new value in numberOfSections or numberOfRows dataSource methods. Every change should be compensated with delete/insert rows(sections) method. And vice versa: if you delete/insert to tableView, you must return corresponding values in dataSource methods. Just as your exception message states:
The number of sections contained in the table view after the update
(1) must be equal to the number of sections contained in the table
view before the update (3), plus or minus the number of sections
inserted or deleted (0 inserted, 0 deleted).
It's because 3+0≠1. In this case you should have deleted two sections to avoid crash.
The error message is actually very helpful. It talks about sections - it expected not to change, but it found a different value after your operation.
Basically, UIKit did check the number of sections and rows before calling your code, runs your code, and then checks if the the new numbers line up with your operations. In you case, it doesn't - because you are calling deleteRows (which should decrease the row count for the given section), but your numberOfSections delegate gives a different result now. So you either want to call deleteSections or keep the sectionCount unmodified.
BTW, it is not forbidden to return 0 as the number of rows for a section - maybe this is just what you want here.
A further note: I don't think that storing the number of sections in an instance variable is a good thing here. It makes for double bookkeeping.

Swift 3.0 Unable to delete a row from a UITableView

I'm using Xcode 8 and swift 3.
I'm getting an assertion failure when I try to delete a row from a UITableView.
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3599.5/UITableView.m:1610
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (25) must be equal to the number of rows contained in that section before the update (25), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Code:
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UserCell = self.tableView.dequeueReusableCell(withIdentifier: "tblCell") as! UserCell!
cell.titleLabel.text = self.animals[(indexPath as NSIndexPath).row]
cell.btnDelete.addTarget(self, action:#selector(ViewController.buttonClicked(sender:)), for: UIControlEvents.touchUpInside);
return cell
}
func buttonClicked(sender:UIButton) {
if let superview = sender.superview {
if let cell = superview.superview as? UserCell {
if let indexPath = self.tableView.indexPath(for: cell){
print("row = ",indexPath.row)
self.tableView.beginUpdates()
self.tableView.deleteRows(at: [indexPath], with: .left)
self.tableView.endUpdates()
self.animals.remove(at:indexPath.row)
}
}
}
}
I have just started learning swift so please help me.
try to move self.animals.remove(at:indexPath.row) before self.tableView.endUpdates() and after self.tableView.beginUpdates() I think that might solve your problem.

Deleting UITableViewCell: NSInternalInconsistencyException?

I'm getting an error when I try to delete a cell from my UITableView. I've looked at numerous links (this one for example is what I'm going off of. Below is my code:
var countdownTime = expirDate - currentDate
timer.setCountDownTime(countdownTime)
println("Count : \(self.offers.count)")
timer.startWithEndingBlock { (countdownTime) -> Void in
//If the times have expired, then delete the row
self.tableView.beginUpdates()
self.offers.removeAtIndex(indexPath.row)
self.tableView.deleteRowsAtIndexPaths([indexPath.row], withRowAnimation: UITableViewRowAnimation.Fade)
self.tableView.endUpdates()
}
timer.start()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return offers.count
}
//Only one section
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
I want to remove the cell when my timer has completed and the offers array is the array that I use to populate the UITableView. I was going off of this Assertion Failure -[UITableView _endCellAnimationsWithContext] which I thought would help me delete/insert rows correctly but I still get an error saying:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Any help would be appreciated!
EDIT: An issue I might be having that I am trying to delete the cell while I am inserting the cell into the UITableView. I am inserting the cell, setting the cell contents, then deleting the cell if the timer for that cell has existed for over 24 hours. This is bad because the cell will then try to insert the next cell when it thinks that there is 1 cell in the table view. Are there any better ways to do this?
EDIT 2: Code for inserting into table:
...//Code to insert into database
override func viewDidLoad() {
super.viewDidLoad()
//setupOffers()
setupOffers { (result, offer) -> Void in
if(result == true){
//Insert each row one by one.
var currentCount = self.offers.count
var indexPaths: [NSIndexPath] = [NSIndexPath]()
indexPaths.append(NSIndexPath(forRow:currentCount, inSection: 0))
self.offers.append(offer)
currentCount++;
//Update based on each table view index.
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Bottom)
self.tableView.endUpdates()
//Method 2 to update uitableview
//self.tableView.reloadData()
}
}
// Do any additional setup after loading the view, typically from a nib.
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.rowHeight = 145.0
}
Add the statement self.offers.removeAtIndex(indexPath.row) before starting the updates on the table view self.tableView.beginUpdates() as
self.offers.removeAtIndex(indexPath.row)
self.tableView.beginUpdates()
As it could happen that you start the update on table view and the number of rows are returned before deleting the object from the array, and thus could result in inconsistency.
I believe this is simply a caching problem. Your fetchedResultController is not going to automatically refetch your results as it caches its results. That means that when tableView:numberOfRowsInSection: is called again, the results count is still returning number even though you deleted an item.
Use this code...
self.tableView.beginUpdates()
self.tableView!.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
self.tableView.endUpdates()
Error when deleting item from tableview in swift
This might helps you :)
Try self.tableView.beginUpdates() after self.offers.removeAtIndex(indexPath.row)
EDIT:
Remove beginUpdates and endUpdates, as self.tableView.deleteRowsAtIndexPaths([indexPath.row], withRowAnimation: UITableViewRowAnimation.Fade) should be enough to refresh the table.
EDIT 2: Check that the count of offers is correct after the update. Print its count before and after you delete the item and after return offers.count.

Resources