Crash when reloading collectionView frequently
In an iOs app I am using a collection view that will refresh frequently when the user clicks on a cell in another collection that is on the screen. The collection view gets the data one row at a time and reloads the collection.
Here is the code in the selection:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
if (collectionView == self.datePickerCollectionView) {
[[self.PickerCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.crtDelta inSection:0]] setSelected:NO];
if (self.crtDelta == indexPath.item) {
// in case the user taps on the day he allready is on
// don't do anything
return;
}
self.crtDelta = indexPath.item;
[self.attriburesRectDict removeAllObjects];
[self.CollectionView.collectionViewLayout invalidateLayout];
[self.CollectionView reloadData];
[self.TableView reloadData];
self.DataDict = [NSMutableDictionary new];
}
// get data from web manager here
}
As you can see, the data dictionaries are reset.
- (void)didReceiveDataForSection:(NSInteger)section withDataArray:(NSArray*)data andRectsArray:(NSArray*)rects
{
self.programsDict[@(index)] = data;
self.attriburesRectDict[@(index)] = rects;
[self.programsCollectionView reloadData];
}
Here 1 line of data is retrieved and added to the appropriate dictionaries for use. This is the layout code:
@interface MultipleLineLayout()
@property (nonatomic, assign) NSInteger numRows;
@end
@implementation MultipleLineLayout {
CGFloat itemHeight;
}
-(id)init {
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
itemHeight = 0.0;
}
return self;
}
-(CGSize)collectionViewContentSize {
NSInteger xSize = self.contentSizeWidth;
NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + PROGRAMS_CELL_SPACING);
return CGSizeMake(xSize, ySize);
}
-(void)prepareLayout
{
[super prepareLayout];
[self.collectionView setBounces:NO];
if (self.collectionView) {
self.numRows = [self.collectionView numberOfSections];
}
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes *a = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
a.frame = [self.dataSource rectForAttributesForRow:path.row inSection:path.section];
return a;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray* attributes = [NSMutableArray array];
if (itemHeight == 0.0) {
itemHeight = [self.dataSource heightOfProgramCell];
}
NSUInteger startRow = floorf(rect.origin.y / itemHeight);
NSUInteger endRow = ceilf(CGRectGetMaxY(rect) / (CGFloat)itemHeight);
if (endRow >= self.numRows) {
endRow = self.numRows ;
}
for (NSUInteger r = startRow; r < endRow; r++)
{
NSUInteger noProgs = [self.dataSource numberOfElementsInSection:r];
for (NSUInteger c = 0; c <noProgs; c++)
{
UICollectionViewLayoutAttributes* o = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:c inSection:r]];
if(o.size.width > 0) {
[attributes addObject:o];
}
else {
break;
}
}
}
return attributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return NO;
}
@end
The problem is that sometimes if you switch data very often (short for the collection view code shown in the 1st code snippet) (for example, 2 branches per second), the application crashes:
*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit/UIKit-2903.23/UICollectionViewData.m:341
2014-08-21 12:14:33.052 du View[365:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView recieved layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x18c1be10> {length = 2, path = 0 - 0}'
*** First throw call stack:
(0x2dd4cf4b 0x385c36af 0x2dd4ce25 0x2e6f4fe3 0x3051c953 0x3051c063 0x3051b8c7 0x304bfda3 0x30146c6b 0x3014247b 0x3014230d 0x30141d1f 0x30141b2f 0x3013b85d 0x2dd181cd 0x2dd15b71 0x2dd15eb3 0x2dc80c27 0x2dc80a0b 0x32981283 0x30524049 0x14c529 0x38acbab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
I believe that this is some kind of hindrance between recharges in didSelectCell and reloads in the received data. I cancel all data requests when switching, but after a while it seems that the collection view enters this confusing state and crashes.
How can I prevent this crash?
source to share