61

In the course of trying to unload one batch of images from my collection view and then replace them with another batch, I run into an error where, depending on whether the original or subsequent group of images was more or less than the intended replacement, an assertion error occurs which says:

*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], 
/SourceCache/UIKit_Sim/UIKit-2891.1/UICollectionViewData.m:341
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', 
reason: 'UICollectionView recieved layout attributes for a cell with an 
index path that does not exist: <NSIndexPath: 0xb141c60> {length = 2, path = 0 - 2}

In this case the existing list of images count was 5 and the new list of images count was 2. So, when it got to the third image - the exception occurred - indicating that the UI CollectionViewDataDelegate did not know of the change in the data stream.

Any suggestions about how to make sure the new images will be referenced by the UICollectionView? Of course I have called 'reloadData'…

Thank you

user1686700
  • 758
  • 1
  • 5
  • 5
  • 1
    It certainly appears that some vestige of stale data is being accessed by the object - thus the search for a cell, the index of which can be out of range for the current data referenced in the section. In my case, after many experiments the remedy appears to be to "[self.collectionView reloadSections:sections];". After I did this, the exceptions no longer were being asserted when I swiped the collectionView left or right. – user1686700 Aug 22 '13 at 10:02
  • I am having a similar issue. I have two UICV which display the same data. When the user changes the date within the app, I request new data and then call reloadData on both CV. This worked great up through iOS6.1. When I run the apps on newer I get the error mentioned above. Switching to reloadSections:sections fixes it but adds unwanted lag and animation (custom flow layout). Have you had any progress with this at all? – VaporwareWolf Sep 11 '13 at 16:53
  • I get this issue when I return layout attributes having an index path that should not exist based on the number of sections and number of items in each section returned by the data source! Took me a while to figure this out. If you have a dynamic model, make sure sections/item paths align with layout attribute paths. – andrewz Dec 20 '16 at 22:51

21 Answers21

85

I run in the same problem. Code runs under 6.1 and crashes under 7.0 I solved the issue the following way:

In the function

-(NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView

I call

[myCollectionView.collectionViewLayout invalidateLayout];

That´s all.

pkamb
  • 26,648
  • 20
  • 124
  • 157
Dominic Sander
  • 2,684
  • 1
  • 15
  • 29
  • 1
    Thank you! I knew I had to invalidate the layout, but only calling it in this method worked for me. – Nikola Kirev Oct 18 '13 at 20:27
  • 7
    For me, it worked to call `invalidateLayout` just before my call to `reloadData`. – Jesse Rusak Nov 11 '13 at 18:09
  • 1
    Can anyone explain why `invalidateLayout` fixed the issue? I had the same issue when tried to change the `dataSource`, `delegate` and `collectionViewLayout`. – alexcristea Jan 28 '14 at 10:10
  • 1
    @alexcristea - It depends on whether your layout changes too. If your data change is just appending to the end, then you will not need to - if data is inserted above/within your current scroll position, then your cells for those index paths will move, and hence the layout is invalid. – Nick H247 Apr 07 '14 at 11:44
  • 14
    Personally think its a bad idea to put the invalidate in numberOfSectionsInCollectionView: it may be called way too often. Only call it when the data changes as above. – Nick H247 Apr 07 '14 at 11:52
  • 3
    For me it's only working if I call invalidateLayout from numberOfSectionsInCollectionView. If i call it anywhere else (including before reload data method on main thread etc etc) it crashes. – Fidel López Sep 13 '15 at 20:36
  • 1
    I'm facing the same issue, even after calling invalidateLayout from numberOfSectionsInCollectionView or in viewDidLayoutSubviews or just before reloading the data. Its happen only in iOS 10. Can anyone suggest the solution? – MilanPanchal Oct 25 '16 at 09:36
  • 1
    For me, it worked to call invalidateLayout just AFTER my call to reloadData. NOT before. – etayluz Dec 04 '16 at 06:54
  • This is the only way I got rid of the crash. – Roland T. Feb 06 '17 at 16:33
22

With iOS 10 and 11 this helps:

collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()

Invalidate layout should be AFTER reload data.

Michał Kwiecień
  • 2,234
  • 1
  • 17
  • 20
21

Both Dominic Sander and user1544494 are right and their solutions are good.

Unfortunately, I've noticed that if you set minimumLineSpacingForSectionAtIndex or minimumInteritemSpacingForSectionAtIndex, the look of your collectionView is going to break (sooner or later).

Putting invalidateLayout in viewWillLayoutSubviews answers this question and helps preserving the look of viewCollection.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [viewCollection.collectionViewLayout invalidateLayout];
}
Community
  • 1
  • 1
Giuseppe Garassino
  • 2,214
  • 1
  • 23
  • 42
  • I like this solution very much, clean and intuitive. Thanks! – phatmann Feb 11 '14 at 18:59
  • 4
    after putting that to my collection view controller i've got method `-viewWillLayoutSubviews` to be invoked infinitely. What should the view hierarchy be, to make it work? – igrek Sep 02 '15 at 13:50
12

It's simple. Just like that below sentence.

'UICollectionView recieved layout attributes for a cell with an 
index path that does not exist: <NSIndexPath: 0xb141c60> {length = 2, path = 0 - 2}

It's mean that there is no indexPath(0,2) on dataSouce. But, your UICollectionViewLayout return a UICollectionViewLayoutAttributes for indexPath(0,2).

You should return UICollectionViewLayoutAttributes just only exists on dataSouce.


I think that It was changed from iOS7.

TopChul
  • 320
  • 1
  • 14
  • 3
    You're absolutely right. One has to update the underlying datasource/the layout as well. – asp_net Oct 07 '13 at 15:16
  • My coworker found another case last month. updating contentInsets of UICollectionView will cause update layout. So, (1)dataSourceChanged, (2)setContentInsets -> Crash with the exception. (1)dataSourceChanged, (2)reloadData series(Section, insertXXX and so on) (3)setContentInsets -> safe. This case may encountered from iOS 11. – TopChul Feb 06 '18 at 04:36
10

My problem was that I had two UICollectionViews inside one UIViewController. And I had both UICollectionViews connected to the same UICollectionViewLayout subclass. I fixed this problem by changing each UICollectionView to have their own UICollectionViewLayout subclass.

Source: This Question

Community
  • 1
  • 1
Johann Burgess
  • 541
  • 6
  • 17
4

I fixed this crash by updating my collection view data source :

- (NSInteger)collectionView:(UICollectionView *)collectionView
     numberOfItemsInSection:(NSInteger)section
{
    [collectionView.collectionViewLayout invalidateLayout];
    return collectionArray.count;
}
Greg
  • 8,695
  • 6
  • 45
  • 89
megha
  • 445
  • 5
  • 16
2

I had the same crash.

In my app, the problem was that I didn't empty the array with UICollectionViewLayoutAttributes. I'm using it in the prepareLayout() method to store the layout attribute for each cell.

var itemAttributes: Array<UICollectionViewLayoutAttributes> = Array<UICollectionViewLayoutAttributes>()

With just self.itemAttributes.removeAll() in the first line of prepareLayout, it works.

Beuj
  • 602
  • 7
  • 20
2

I've experienced this problem after modifying the contents of the Collection View. The solution that worked in my case was to invalidate the layout after reload. Doing it before the reload will not work.

[collectionView reloadData];

//forces the layout attributes to be recalculated for new data
[collectionView.collectionViewLayout invalidateLayout];
ricosrealm
  • 1,526
  • 1
  • 15
  • 25
1

The solution I found was to ensure the indexPath I was creating in the layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? method is valid for the row. Previously, I was using (where i is my loop counter):

var indexPath = NSIndexPath(index: i)
var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)

But updating it to use the following solved it:

var indexPath = NSIndexPath(forRow: i, inSection: 0)!
var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
Sam Youtsey
  • 856
  • 2
  • 12
  • 32
1

I was able to solve this issue by creating a subclass of UICollectionViewFlowLayout and overriding this method to return YES:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}
Snowman
  • 29,431
  • 43
  • 165
  • 290
0

Make sure you update the contentSize of your collectionViewLayout. So after getting new images (2, instead of 5), recalculate the contentSize and set it.

Greg
  • 8,695
  • 6
  • 45
  • 89
0

I have encountered with this issue and it was pretty annoying. My fix is to forget about UICollectionViewController and use instead regular UIViewController with UICollectionView inside.

Pavel Sharanda
  • 909
  • 10
  • 8
0

I've had this bug too and found a workaround. For me, the UICollectionView was launching this under iOS 7, working perfectly on iOS 8.

Check this article: What's causing this iOS crash? UICollectionView received layout attributes for a cell with an index path that does not exist

In 2 words: Auto-Layout. Disable it on the view containing the UICollectionView and for me, it worked.

Community
  • 1
  • 1
Eric Giguere
  • 636
  • 5
  • 9
0

The collectionViewLayout caches the layout attributes. In viewwillappear . Create a new instance of collectionViewLayout and assign it to collectionview.collectionViewLayout In this way all the cached attributes will purge before the reload Your problem might be resolved. Worked for me , especially when you are using other collectionViewLayout libraries.

Ankish Jain
  • 10,825
  • 4
  • 31
  • 34
0

I had similar problem (using Swift2.0, XCode 7).

The app crashed with UICollectionView received layout attributes for a cell with an index path that does not exist...

In my case, since I used the storyboard, it just turned out that I forgot to connect the IBOutlet that was defined in my viewController with the actual collectionView defined in the storyboard. Connecting the two fixed the problem.

da-na
  • 242
  • 2
  • 8
0

I have figured it out. If you are using nib/xib to organize your UITableViewCell and nested UICollectionView,you can avoid this error by overriding this method.

- (void)prepareForReuse {

    [super prepareForReuse];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

Hope it helps.

tounaobun
  • 13,445
  • 9
  • 48
  • 75
0

It's mean that there is no indexPath(0,2) on dataSouce. But, your UICollectionViewLayout return a UICollectionViewLayoutAttributes for indexPath(0,2). By TopChul

That's Right! For Me, the issue happened because I use same collection layout(instance) for two collectionView! So that layout confused between two collection View.

It work fine after I use different layout between different collection view.

0

I ran into something similar when trying to duplicate a collection view into another storyboard.

'UICollectionView received layout attributes for a cell with an index path that does not exist: {length = 2, path = 1 - 0}'

At first, I am looking for a quick fix. Tried copy paste various StackOverflow answers.

But I wrote my own layout class. So I try to debug discreetly, it could be my implementation to blame, right? Found that numberOfSections method was never called. The collection view assumed that it had only one section.

Then I found view controller class forgot to conform to UICollectionViewDataSource. Though the dataSource was hooked up in the storyboard but probably the view controller class will be downcast, like if let ds = dataSource as? UICollectionViewDataSource {ds.numberOfSections...}, which would fail silently.

So I added the conformance to UICollectionViewDataSource and everything works fine. My guess could be inaccurate. But the lesson is whenever there's a bug you're not familiar with, settle down and understand it. UICollectionView received layout attributes for a cell with an index path that does not exist, it means exactly what it says. Not that hard right? Don't try to find a silver bullet, like many of the answers here. They are all great but your code is the real battle place.

jchnxu
  • 793
  • 1
  • 10
  • 19
0

I run into the same problem when I use UICollectionViewFlowLayout as CollectionView's collectionViewLayout.

Declare the parent viewController implemented UICollectionViewDelegateFlowLayout and assign it as collectionView's delegate can solve this problem.

Jared Beck
  • 13,951
  • 6
  • 59
  • 85
ChaoTangChang
  • 221
  • 3
  • 6
0

In my case i had a custom UICollectionViewFlowLayout. After removing cells from the collectionView the app crashed. The fix was to removeAll() previously calculated attributes. So, first line after override func prepare() is arrayHoldingYourAttributes.removeAll().

Pantelis Proios
  • 1,339
  • 1
  • 20
  • 31
-2

Hollo, I have the same issue while insert a collectionView into another collectionView and reloadData in main queue. Finally I reloadData before a new data convert to collectionView.

dispatch_async(dispatch_get_main_queue(), ^{
    [_collectionView reloadData];
});

_notes = notes;

[_collectionView reloadData];