Assertion Failure in UICollectionViewData
validateLayoutInRect
on iOS7.
I am trying to delete all UICollectionView
items, one by one, using a for
loop; I posted my code below. I delete the UICollectionView
items using deleteItemsAtIndexPaths
. It's working perfectly on iOS6, but crashes in iOS7 with this exception:
Assertion Failure in UICollectionViewData validateLayoutInRect
I delete the object from collectionArray
then self.collectionView
, one by one, using indexPath
. When I delete the 4th object its raises Assertion failure
on iOS7. Here I am using performBatchUpdates
.
Please help me get the proper result in iOS7. Share proper code.
Thanks in advance.
try {
for (int i=count-1; i>=0; i--) {
[self.collectionView performBatchUpdates:^(void){
[collectionArray removeObjectAtIndex:i]; // First delete the item from you model
[self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]]];
} completion:nil];
[self.collectionView reloadData];
}
}
@catch (NSException *exception) {
}
@finally {
}
I actually got this crash one time not because I was returning zero for a number of sections or items in a section but because I was reusing a flow layout like this for more than 1 collection view:
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
Collection1 = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 50.0f) collectionViewLayout:flowLayout];
[Collection1 setDataSource:self];
[Collection1 setDelegate:self];
[self.view addSubview:Collection1];
Collection2 = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, self.view.frame.size.height) collectionViewLayout:flowLayout];
Collection2.backgroundColor = [UIColor whiteColor];
Instead if I create a new flow layout for each UICollectionView I avoid this crash. Hopefully that might help someone
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
Collection1 = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 50.0f) collectionViewLayout:flowLayout];
[Collection1 setDataSource:self];
[Collection1 setDelegate:self];
[self.view Collection1];
UICollectionViewFlowLayout *flowLayoutVert = [[UICollectionViewFlowLayout alloc] init];
[flowLayoutVert setScrollDirection:UICollectionViewScrollDirectionVertical];
Collection2 = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, self.view.frame.size.height) collectionViewLayout:flowLayoutVert];
in iOS 10 you must disable the prefetchingEnabled:
// Swift
if #available(iOS 10, *) {
collectionView.prefetchingEnabled = false
}
//Obj C
if ([self.collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) {
self.collectionView.prefetchingEnabled = false;
}
It looks like you probably want to do this:
[self.CollectionView performBatchUpdates:^(void) {
for (int i = count - 1; i >= 0; i--) {
[collectionArray removeObjectAtIndex:i]; // First delete the item from you model
[self.CollectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]]];
}
} completion:nil];
So that all the updates are performed together. Otherwise you will end up trying to perform several lots of batch updates on top of each other.
![](https://www.manongdao.com/static/images/pcload.jpg)
You should implement invalidateLayout in your layoutClass
and remove all kinds of UICollectionViewLayoutAttributes
items in your config.
- (void)invalidateLayout{
[super invalidateLayout];
[self.itemsAttributes removeAllObjects];
}
try to call [yourCollectionView.collectionViewLayout invalidateLayout];
You should implement invalidateLayout in your layout class and remove all kinds of UICollectionViewLayoutAttributes
items in your config.
- (void)invalidateLayout{
[super invalidateLayout];
[self.itemsAttributes removeAllObjects];
}
For a better way to implement invalidateLayoutWithContext
, see more about UICollectionViewLayoutInvalidationContext
.
From Apple Developer documentation:
When implementing a custom layout, you can override this method and use
it to process information provided by a custom invalidation context.
You are not required to provide a custom invalidation context but might
do so if you are able to provide additional properties that can help
optimize layout updates. If you override this method, you must call
super at some point in your implementation.
In my case following delegate was missing:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
[collectionView.collectionViewLayout invalidateLayout];
return 1;
}