Search code examples
objective-cuicollectionviewnsarrayuicollectionviewcell

Adaptable UICollectionViewCell Background image - locked/unlocked game levels


I have very little experience working with UICollectionView's but I've managed to create a scrollable interface that displays 100 buttons for 100 levels, 4 across, 25 down (well, they get generated on-the-fly as you know). The type of level is called Tortoise just so you're wondering what the hell that stands for later on. For Tortoise, there are only 20 levels.

Right now, the cell data string that I use to display the number gets placed over a regular background for the cell (indicating the level has been unlocked but not completed).

I have 2 other images I'd like to use as background images (one is a lock image where no number string appears, and the other is the same background as above just with a small checkmark for completion (along with the number string on top)).

To brief you, I'm using Core Data to keep track of a couple set of objects, along with whether a level is locked or unlocked. I call a method (right as I enter my Tortoise UICollectionView) called figureOutLocks which stores either a 0, 1, or 2 NSNumber object in this array self.lockArray. Here's that method:

    - (void)figureOutLocks {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSManagedObjectContext* managedObjectContext = [(AppDelegate*)[[UIApplication sharedApplication]
                                                                    delegate] managedObjectContext];
        NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Score" inManagedObjectContext:managedObjectContext];
        [fetchRequest setEntity:entityDescription];
        for (int i = 0; i < 20; i++) {
            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %d AND %K == %d",
                                       @"level", i, @"typeOfLevel", 0];
            [fetchRequest setPredicate:predicate];
            NSError *error = nil;
            Score* lockInfo = [[managedObjectContext executeFetchRequest:fetchRequest
                                                                     error:&error] lastObject];

            lockInfo.levelCompleted = [lockInfo valueForKey:@"levelCompleted"];
            lockInfo.lockedLevel = [lockInfo valueForKey:@"lockedLevel"];
            NSInteger complete = [lockInfo.levelCompleted integerValue];
            NSInteger locked = [lockInfo.lockedLevel integerValue];

            if ((locked == 0) && (complete == 0)) {
                // level is unlocked but not complete (does not have any saved scores)
                // lockArray gets a 0
                [self.lockArray addObject:[NSNumber numberWithInteger:0]];
            } else if ((locked == 1) && (complete == 0)) {
                // level is locked which implies it is not complete
                // lockArray gets a 1
                [self.lockArray addObject:[NSNumber numberWithInteger:1]];
            } else if ((locked == 0) && (complete == 1)) {
                // level is complete thus it is unlocked
                // lockArray gets a 2
                [self.lockArray addObject:[NSNumber numberWithInteger:2]];
            }
        }
    }

To brief you again, the first level is unlocked and not completed, and the other levels are locked (thus not completed).

Also, I created a NSArray *dataArray that contains string objects 1-20, and a NSArray *compareArray that contains NSNumber objects 1-20. My lockArray is NSMutableArray.

Furthermore, I decided to make 2 separate UICollectionViewCell subclasses in order to use both the regular background and the locked background. I didn't add the completion background subclass because I wanted to make sure the locked background works.

Here's the main method:

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

        UICollectionViewCell *regular;

        static NSString *cellIdentifier = @"tortoiseCell";
        static NSString *tortIdentifier = @"tortoiseLocked";
        TortoiseCell *cell = (TortoiseCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
        TortoiseLocked *lockCell = (TortoiseLocked *)[collectionView dequeueReusableCellWithReuseIdentifier:tortIdentifier forIndexPath:indexPath];

        NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];
        NSString *cellData = [data objectAtIndex:indexPath.row];
        NSMutableArray *locks = [self.compareArray objectAtIndex:indexPath.section];
        NSNumber *locksData = [locks objectAtIndex:indexPath.row];
        NSInteger locked = [locksData integerValue];
        NSInteger lock = [[self.lockArray objectAtIndex:locked] integerValue];

        if (lock == 0) {
           [cell.buttonClick setTag:indexPath.row];
           [cell.buttonClick setTitle:cellData forState:UIControlStateNormal];
           [cell.buttonClick setBackgroundImage:[UIImage imageNamed:@"TortoiseLevels.png"]
                                forState:UIControlStateNormal];
           [cell.buttonClick setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
           [cell.buttonClick.titleLabel setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:25]];
           [cell.buttonClick addTarget:self action:@selector(buttonPressedSoWhatNumber:)
               forControlEvents:UIControlEventTouchUpInside];
           cell.buttonClick.layer.cornerRadius = 8;
           cell.buttonClick.layer.masksToBounds = YES;
           [cell addSubview:cell.buttonClick];
           cell.layer.shouldRasterize = YES;
           cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
           regular = cell;
        } else if (lock == 1) {
           [lockCell.tortoiseLock setTag:indexPath.row];
           [lockCell.tortoiseLock setBackgroundImage:[UIImage imageNamed:@"TortoiseLock.png"]
                                forState:UIControlStateNormal];
           lockCell.tortoiseLock.layer.cornerRadius = 8;
           lockCell.tortoiseLock.layer.masksToBounds = YES;
           [lockCell addSubview:lockCell.tortoiseLock];
           lockCell.layer.shouldRasterize = YES;
           lockCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
           regular = lockCell;
        }

        return regular;
    }

Is what I've done even possible? How do I make this work if it is? I tried using one UICollectionViewCell subclass and just programmatically change backgrounds but that didn't work, that's why you see what you see with the larger if statements. Any thoughts?


Solution

  • You definitely don't need to dequeue both cells from the table - you'll only ever be using one of them. Your code should be structured more like this:

    static NSString *cellIdentifier = @"tortoiseCell";
    static NSString *tortIdentifier = @"tortoiseLocked";
    
    -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                     cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
        UICollectionViewCell *regular;
    
        NSMutableArray *locks = [self.compareArray objectAtIndex:indexPath.section];
        NSNumber *locksData = [locks objectAtIndex:indexPath.row];
        NSInteger locked = [locksData integerValue];
    
        NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];
        NSString *cellData = [data objectAtIndex:indexPath.row];
        NSInteger lock = [[self.lockArray objectAtIndex:locked] integerValue];
    
        if (lock == 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 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
            [cell.buttonClick.titleLabel setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:25]];
            [cell.buttonClick addTarget:self action:@selector(buttonPressedSoWhatNumber:)
                       forControlEvents:UIControlEventTouchUpInside];
            cell.buttonClick.layer.cornerRadius = 8;
            cell.buttonClick.layer.masksToBounds = YES;
            [cell addSubview:cell.buttonClick];
            cell.layer.shouldRasterize = YES;
            cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
            regular = cell;
    
        } else if (lock == 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.cornerRadius = 8;
            lockCell.tortoiseLock.layer.masksToBounds = YES;
            [lockCell addSubview:lockCell.tortoiseLock];
            lockCell.layer.shouldRasterize = YES;
            lockCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
            regular = lockCell;
        }
    
        return regular;
    }
    

    Also, since you have already set up two types of cell prototype on the storyboard, why bother setting each cell up programmatically? Just make them look how you want on the storyboard, and add the level number in the code.