What would tableView.reloadRows(at: [indexPath], with: .none) to crash like this? - ios

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.

Related

Issue with deleting multiple cells in collection view at once

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

Get first row in UITableView where unknown number of first sections are empty

I need the index path of the first row in a tableview under these conditions:
The first row could be off screen and so not in calls to methods that return an array of visible rows.
The first row's section is unknown, could be greater than 0, and the total number of sections is variable. This results from prior sections returning 0 to [UITableViewDataSource numberOfRowsInSection:] calls.
I could cycle through each section looking for rows but was wondering if UITableView has a better way to do this?
Based on the comments, it seems you want to know the index path of the row in the first section of the table view that has 1 or more rows.
Your only option is to iterate through your data source section by section until the section has 1 or more rows in it. Then the desired row will have an indexPath of row 0 and the section you just found.
There is no shortcut available through the table view to get this info.
Though there is one possible shortcut but it requires that your table view use "Plain" style (not "Grouped") and your table view can't have any header or footer views for any of the sections. If all of that is true, you can get the indexPath of the row you want by using the UITableView indexPathForRowAtPoint: and pass in CGPointMake(5, 5). That will give the index path of the topmost row in the whole table view.

iOS UITableView - crash when inserting new section with error message which may indicate that I should call moveSection:toSection: method

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.

UITableView multiple insertSections to same index

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.

UITableview reloadRowsAtIndexPaths crash when table data gets updated

I have a table view with each rows having image and some text. The images are loaded asyc. As soon as image is fetched from server, the delegate methods gets called. The delegate method contains the index path which initiated the image fetching, so that I can reload only those cells. I have extra check to make sure that once the image is fetched, the data source contains enough data so that index path doesn't go out of bounds. Despite setting this condition the app crashes.
There is a chance that my table data gets updated before the image is fetched. I know this is cause of the issue, but I am not sure why this is making a crash despite adding a check before
reloadRowsAtIndexPaths?
The error is:
Fatal Exception NSInternalInconsistencyException Invalid update: invalid number of rows in section 2. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (6), 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).
Can some one share any inputs on this?
Code snippet that reloads the table
// this method will be called on main thread
- (void)loadImageAtIndexPath:(NSIndexPath *)inIndexPath {
if(inIndexPath.row < dataSource.count) {
[self.listView reloadRowsAtIndexPaths:[NSArray arrayWithObject:inIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
}

Resources