I have a dataset that I want to display in an UITableView. All new sets of data I want to insert as a section 0. Often, I will get many sets, and then I want to push them on the top one at a time. This looks like
tableView.beginUpdates()
repeat {
data.append(dataProvider.nextDataSet())
tableView.insertSections(NSIndexSet(index:0), withRowAnimation: .Automatic)
} while dataProvider.hasMoreData
tableView.endUpdates()
While my data array is exactly like I would like it, my app crashes saying something like:
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 (5) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (2 inserted, 0 deleted).'
I have made a little sample project on GitHub that recreates this problem by doing
numSections += 1
tableView.insertSections(NSIndexSet(index:0), withRowAnimation: .Automatic)
five times (link) where numSections is the number of sections, and the number of rows is always three per section. Again it says that I have inserted only 2 sections, while in fact I did insert 5.
What is it I am misunderstanding here? Why does tableView think I only gave it 2 new sections? Of course, if I only give it 2 sections, it thinks I only gave it 1. :-I
The section index you insert doesn't matter as far as the order of your data being displayed is concerned, that's controlled by your data array. You should insert the sections at different indexes, so use the numSections variable to control that.
I expect if you move the begin and end updates into the loop it will also work. The problem is you don't know how the table view works internally, the issue you see isn't documented as far as I know, but it's illogical to insert sections at indices which don't match your underlying data.
The insertSections will be actually executed at the end of endUpdates(). So in actual scenario, the sections isn't inserted and you are specifying as the sections have been increased.
This will work. If you know the number of sections to be inserted, then use this method
tableView.beginUpdates()
numSections += 5
tableView.insertSections(NSIndexSet(indexesInRange: NSMakeRange(0, 5)), withRowAnimation: .Automatic)
tableView.endUpdates()
The above code is for the github code. It can be altered for your actual code. Do not call insertSections() in a loop or multiple times.
Related
I’m trying to make sense of an error in Fabric:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (40) must be equal to the number of rows contained in that section before the update (20), plus or minus the number of rows inserted or deleted from that section (1 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
specialized NewInboxViewController.tableView(UITableView, didSelectRowAt : IndexPath)
Happens on this line in didSelectRow:AtIndexPath:
tableView.reloadRows(at: [indexPath], with: .none)
The table view behavior is:
view model loads 20 rows
when the user scrolls to 70% of the table view, we load 20 more rows, an isDownloading bool is toggled to true, set to false when call finishes, then the table view reload
can commit edits is false while isDownloading is true, otherwise the user can swipe to delete the row
when the user pulls to refresh, we blow away whatever is stored in the array the cells are pulled from and replace with 20 recent items from the API call (edited)
You are telling the table view to reload one row but you added 20 new rows to the data model. That's what the error means. It says:
"before you called reloadRows there had been 20 rows in the data model. Now I see there are 40 rows in the data model but you are only telling me to reload (insert and delete) 1 row."
You should instead be calling tableView.insertRows(at:) with an array of 20 new index paths, one for each newly added row.
I'm allowing users to select/deselect cells in my collection view and then hit a save/delete button.
When I'm selecting rows, I add them to a dictionary
var selectedIndexes = Dictionary<IndexPath, String> ()
and when I deselect the row I set the selectedIndexes[indexPath] = nil
When the user hits delete I run
for index in selectedIndexes.keys {
indexesToDelete.append(index)
}
collectionView.deleteItems(at: indexesToDelete)
This goes into the selectedIndexes dictionary, grabs the indexes, adds those indexes into an array of indexes "indexesToDelete", and then after the forloop is over, I'm deleting that array of indexes.
When I run this I get:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (5) must be equal to the number of items contained in that section before the update (5), plus or minus the number of items inserted or deleted from that section (0 inserted, 2 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).'
I've printed everything out and the indexesToDelete are the correct indexes for what I'm trying to delete. I don't fully understand what the error message is saying.
What it is saying is that your collection view is no longer in sync with your data source. So you also need to update whatever you are using as your dataSource to reflect your changes and then call deleteCells.
for index in selectedIndexes.keys {
indexesToDelete.append(index)
}
yourCollectionViewDataSourceArray.remove(at: index)
collectionView.deleteItems(at: indexesToDelete)
if your collectionView data source is yourCollectionViewDataSourceArray, both are synced together. Every time collectionView reloads data it uses the the function with argument numberOfItemsInSection. When it finds out that your array.count is different than the total of items after deletion it gives you this error. Take it as a rule every time you want to update your collectionView by code, update the data array before updating it. Hope that helps
I reload, insert and delete table view's rows and sections in a single begin/end update block. I do not call moveSection:toSection: nor moveRowAtIndexPath:toIndexPath: method.
I am aware that deletion and reloading of existing cells are being executed first, regardless of the order of method calls in update block. I do not insert rows for the new section that is inserted. Also, I am updating data source before table view update block.
Deletion and reloading work just fine for me, but I have a problem with inserting new sections. I got error messages of type:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 5. 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 (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
It is interesting that I do not delete or reload any table view's rows and sections in the corresponding table view update block. Also, there is 1 row in all sections that come before section 5. New section was inserted at index 0. So it seems to me that I might be missing moveSection:toSection: method call..?
I couldn't find more complex examples of simultaneous inserting, deleting and reloading rows and sections in a single update block on Internet, so I would really appreciate any suggestions and links to the examples that could guide me how to handle more complex table view updates.
If I try to insert an item into a UICollectionView when there are 0 sections and 0 items, I get an assertion failure.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of sections. The number of sections contained in the collection
view after the update (1) must be equal to the number of sections
contained in the collection view before the update (0), plus or minus
the number of sections inserted or deleted (0 inserted, 0 deleted).'
A classic. The problem is, I'm modifying my data source and then inserting the item directly afterwards. The showFirstCell property is checked in all of the necessary data source methods and it isn't modified anywhere else.
self.showFirstCell = true
self.collectionView.insertItemsAtIndexPaths([NSIndexPath(forItem: 0, inSection: 0)])
Wrapping this in performBatchUpdates changes nothing.
I would like for this item to be added in an animated fashion. I don't think I should have to first check if there are items already in place, but doing so and calling reloadData instead does work, but it's not perfect.
Your number of sections don't match up.
Once you have decided to enter a section to the UICollectionView, you should return the same number in your numberOfSections function as well.
What's happening right now is that you insert a cell, and it calls your numberOfSections function again, where you are still returning 0, and it promptly crashes.
The reason why it didn't work and gave you an error is because your numberOfSections is set to 0 and it didn't update yet when you add the cell.
To fix that, just create (if you don't have already) an NSMutableArray, and for numberofSections, return that mutableArray. Then whenever you add an object to the collectionView, just add an object to the mutableArray of sections.
Next and most important part is: Anytime you change, add, or remove something from the collectionView, just do a beginUpdat and endUpdate.
If you want to insert item from your CollectionView, you need to
insert that item from your data source which is in your array. In this example I inserted the item to myArray
then I inserted it also to myCollectionView. It's working because
the total number of items from your array (data source) is equal to
the number of items in the collectionView.
myArray.insert(valuetoinsert, atIndex: 0)
let path:NSIndexPath = NSIndexPath.init(row: 0, section: 0)
myCollectionView.insertItems(at: [path as IndexPath])
This is driving me nuts, I have sections in my UITableView, but this insertion works when I don't have sections.
Basically I'm doing:
[self.tableView insertRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationRight];
I'm getting this error:
The number of sections contained in the table view after the update (5) must be equal to the number of sections contained in the table view before the update (4), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
I thought it would just work, but this doesn't make sense. What can you guys make of this?
You are returning different values from your numberOfSectionsInTableView: method before and after you send the insertRowsAtIndexPaths:withRowAnimation: method.
If you are creating an entirely new section in the table view, you must insert it by sending insertSections:withRowAnimation: message to the table view.
Whenever you make a insertRowsAtIndexPaths:withRowAnimation call, you also need to modify the data source that backs the table with a similar addition. This ensures that tableView:numberOfRowsInSection: returns n before the addition and n+1 after the addition. Anything besides a consistent result with throw the error you described.