Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting issue with UICollectionView reloadData

When dealing with a UICollectionView in my app I've ran into a strange problem related to reloading data. After lot's of debugging and analyzing logs I've come to the conclusion that if reloadData is immediately followed by insertItemsAtIndexPaths the dreaded error below is guaranteed to occur:

Name: NSInternalInconsistencyException Reason: Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (1) must be equal to the number of items contained in that section before the update (1), plus or minus the number of items inserted or deleted from that section (1 inserted) ...

They only way for this to happen consistently is that internally the UICollectionView is still busy with reloadData when the call to insertItemsAtIndexPaths arrives. The fact that "collectionView:numberOfItemsInSection" is called twice in a row before the insertItemsAtIndexPaths completes seems to support this since that method is never called twice in a row in call other cases.

Has anyone seen similar behavior or can confirm my analysis or even suggest a proper workaround?

Update: Any yes I've made sure that all relevant invocations occur on the main thread.

Update 2: Since the reasoning behind getting into this situation at all has been questioned: I'm using Monotouch and the code in question is intended to keep generic .Net Collections firing this event into the appropriate calls to keep the UICollectionView bound to the collection in sync. When the source collection is cleared it reacts with a Reset action, followed by one or more Add actions when items get inserted into it which leads to the problem outlined above. Hope this helps.

like image 711
Oliver Weichhold Avatar asked Sep 10 '13 09:09

Oliver Weichhold


1 Answers

When you call insertItemsAtIndexPaths (removeItemsAtIndexPaths is analogous), you are telling your collectionview that its datasource now has more items available, and that it should insert these available items at the indexpaths you specify.

It checks your datasource whether that statement is true, and if it detects that the amount of old items plus the amount of items you say you inserted is not equal to the amount of new items, it says it can't do the update, as you lied about how many items you changed.

Now, what you are doing is you are telling your collectionview is that it should reload all its data from its datasource (with the new data) and right after that, you tell it you inserted x items. That is a false statement, as you just reloaded the collectionview, which updated its amount of items, and thus the amount of items before the update is equal to the amount of items after the update (you're not doing anything), and not increased by the amount of indexpaths you specified.

I hope you're still with me, because here's your solution:

Remove the reloadData before the insertItemsAtIndexPaths, as this breaks its assertions and will throw exceptions when incorrectly used. If you would like to reload the collectionview before inserting items, make sure you perform insertItemsAtIndexPaths right after you change the items in the datasource.

Read up on the documentation for this method here.

like image 64
vrwim Avatar answered Oct 27 '22 00:10

vrwim