Anthony the Kid Anthony the Kid - 1 month ago 8
iOS Question

UICollectionView *Assertion Failure* for not applying attributes on UICollectionViewCell

I am trying to do the following:

A)Level 1 is unlocked (contains background image for unlocked levels).

B)Level 2-20 are locked (contains another background image). They will be unlocked as each successive level is complete.

Initially when I run the app, and go to my

UICollectionView
, everything looks good. When I play level 1, and complete it, I save (through Core Data) the number 0 for an
NSNumber
managed object called
levelLocked
, belonging now to level 2 (0 means unlocked, and the other levels 3-20 should be locked with a 1).

That process of saving whether a level is locked or unlocked works. But when I return to my
UICollectionView
, I do not see the physical change for level 2 (it still has the background image of the lock).

Furthermore, if I return to the
ViewController
before the
UICollectionView
, then re-enter the
UICollectionView
, the app crashes with the error:

*** Assertion failure in -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:],


It appears my cell is not being updated properly (I do not know how to reload data properly). I did register the custom
UICollectionViewCell
nib for both unlocked and locked level types. I checked all spelling (it wouldn't have worked the first time around to begin with if that was the issue).

Here's the code for the cell generation.

static NSString *cellIdentifier = @"tortoiseCell";
static NSString *tortIdentifier = @"tortoiseLocked";

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewCell *regular;

NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];
NSString *cellData = [data objectAtIndex:indexPath.row];

NSMutableArray *ar = [self.lockArray objectAtIndex:indexPath.section];
NSNumber *arLocks = [ar objectAtIndex:indexPath.row];

if ([arLocks integerValue] == 0) {

TortoiseCell *cell = (TortoiseCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.buttonClick setTag:indexPath.row];
[cell.buttonClick setTitle:cellData forState:UIControlStateNormal];
[cell.buttonClick setBackgroundImage:[UIImage imageNamed:@"TortoiseLevels.png"]
forState:UIControlStateNormal];
[cell.buttonClick addTarget:self action:@selector(buttonPressedSoWhatNumber:)
forControlEvents:UIControlEventTouchUpInside];
cell.buttonClick.layer.masksToBounds = YES;
[cell addSubview:cell.buttonClick];
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
regular = cell;

} else if ([arLocks integerValue] == 1) {

TortoiseLocked *lockCell = (TortoiseLocked *)[collectionView dequeueReusableCellWithReuseIdentifier:tortIdentifier forIndexPath:indexPath];
[lockCell.tortoiseLock setTag:indexPath.row];
[lockCell.tortoiseLock setBackgroundImage:[UIImage imageNamed:@"TortoiseLock.png"]
forState:UIControlStateNormal];
lockCell.tortoiseLock.layer.masksToBounds = YES;
[lockCell addSubview:lockCell.tortoiseLock];
lockCell.layer.shouldRasterize = YES;
lockCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
regular = lockCell;
}
return regular;
}


Let me know what you think. I'm using iOS 8.1 with XCode 7.1.2

Answer

Your cellForItemAtIndexPath can return nil if the value in your array is neither 0 or 1. I suspect that this is what is happening, and then the collection view has an assertion failure, since you have given it nil instead of a cell.

The simple fix is to change the else if to else and this will result in a "locked" cell by default.

You could also add a new else clause that logs an error and use that to try and diagnose why you aren't getting 0/1.