iosobjective-cuicollectionviewuicollectionviewcell

prevent cell stacking in UIcollectionView


I've been working on a collection game using apple maps in Objective C. Each item you collect appears in a uicollectionview. it works perfectly fine until you get around 15 or so and have to start scrolling down the view, then I get this weird stacking effect seen in the picture. The kanohi found variable is working correctly and there are 21 cells there for 21 items. Here is my code (or at least the important bits):

//
//  wallOfMasksController.m
//  Matoran Quest
//
//  Created by Job Dyer on 12/11/23.
//

#import "wallOfMasksController.h"
#import "UICollectionViewCell+CollectionViewCell.h"

@interface wallOfMasksController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@end

@implementation wallOfMasksController

NSArray *maskArray; //our mask array
NSArray *selectedMaskArray; //the mask we want to know more about
UIImage *maskImage2; //the colourized mask image
UIImage *outPutMask; //the mask we take to the individual mask viewer
NSMutableArray *imagesInCollection; //an array of all the images
NSMutableArray *collectedMasks2; //a list of the kinds of masks the player has collected. 1 entry for each unique kind of mask (colour as well as type)

- (void)viewDidLoad {
    [super viewDidLoad];
    maskArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"PlayerMasks"]; //get the players masks from the user defaults and make an array of them
    //NSLog(@"monkey: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"PlayerMasks"]);
    _maskGrid.delegate = self;
    _maskGrid.dataSource = self;
    //NSLog(@"%@", maskArray);
    [_maskCount setText:[NSString stringWithFormat:@"Kanohi found: %d", (int)maskArray.count]]; // how many masks do we have?
    imagesInCollection = [[NSMutableArray alloc] init];
    
    collectedMasks2 = [[NSUserDefaults standardUserDefaults] objectForKey:@"PlayerMaskCollectionList"];
    if(collectedMasks2 == NULL){ //if there is no player masks collection key yet
        collectedMasks2 = [[NSMutableArray alloc] init];
        //NSLog(@"refreshing2");
        [[NSUserDefaults standardUserDefaults] setObject:collectedMasks2 forKey:@"PlayerMaskCollectionList"]; //set it if it doesnt exist
        
    }
    [_collectionCount setText:[NSString stringWithFormat: @"Collection: %d/183", (int)collectedMasks2.count]];
}


- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
    //NSLog(@"Got this far");
    CollectionViewCell *cell3 = [_maskGrid dequeueReusableCellWithReuseIdentifier:@"cell3" forIndexPath:indexPath];
    //[cell1.customLabel setText:itemArray[indexPath.row]];
    //make a label
    //UILabel *itemName = [[UILabel alloc]initWithFrame:CGRectMake(91, 15, 0, 0)];
    //
    
    NSArray *maskInterior = [maskArray objectAtIndex:indexPath.row]; //get the mask list to get out the name of the mask
    //NSLog(@"%@", maskInterior)
    NSString *maskNameAndColour = maskInterior[0];
    NSArray *maskColourAndName; //array of seperated name and colour
    NSString *maskName;
    int noColourFlag = 0; //check if mask is special and skip colourizing
    if([maskNameAndColour isEqualToString: @"vahi"]){ //seperate things out if needed
        maskName = @"vahi";
        noColourFlag = 1;
    }
    else if([maskNameAndColour isEqualToString: @"avohkii"]){
        maskName = @"avohkii";
        noColourFlag = 1;
    }
    else if([maskNameAndColour isEqualToString: @"infected hau"]){
        maskName = @"infected hau";
        noColourFlag = 1;
    }

    else{
        maskColourAndName = [maskNameAndColour componentsSeparatedByString:@" "];
        maskName = maskColourAndName[1];
    }
    /*
    //check and replace problem names (legacy). this will cause the app to crash if the string does not have a hyphen added
    if([maskNameAndColour containsString:@"light green"]){
        [maskInterior replaceObjectAtIndex:0 withObject:@"light green"];
        [maskArray replaceObjectAtIndex:indexPath.row withObject:maskInterior];
    }
    */
    
    UIImageView *maskImage=[[UIImageView alloc]initWithFrame:CGRectMake(18, 18, 64, 64)];
    if(noColourFlag == 0){ //if its not a special flag, then colourize it
        UIColor *tempColor = [self colourCaser: maskColourAndName[0]]; //colour the image
        maskImage2 = [UIImage imageNamed:[NSString stringWithFormat: @"%@", maskName]];
        //[maskImage2 imageWithTintColor:tempColor];
        maskImage2 = [self colorizeImage:maskImage2 color:tempColor];
        [maskImage setImage:maskImage2]; //set an image with a colour
        [maskImage2 setAccessibilityIdentifier: maskNameAndColour];
        if(maskImage2 != nil){
            [imagesInCollection addObject: maskImage2];
        }
        else{
            NSLog(@"adding to collection error");
        }
        //NSLog(@"%@", imagesInCollection);
    }
    else{
        [maskImage setImage:[UIImage imageNamed:[NSString stringWithFormat: @"%@", maskName]]]; //get the image named the name of the mask without the colour
    }

    [cell3 addSubview:maskImage];//Add it to the view of your choice.
    
    UILabel *maskNameLabel =[[UILabel alloc]initWithFrame:CGRectMake(25, 70, 50, 50)];//Set frame of label in your view
    //[itemName setBackgroundColor:[UIColor lightGrayColor]];//Set background color of label.

    @try {
        [maskNameLabel setText: [NSString stringWithFormat: @"%@",maskNameAndColour]];
    }
    @catch (NSException *exception) {
        [maskNameLabel setText: @""];
    }
    @finally {
      //Display Alternative
    }
    //NSLog(@"here: %@", maskArray);
    
    //[maskName setText: @"mask"];
    [maskNameLabel setAdjustsFontSizeToFitWidth:true];
    [maskNameLabel setFont:[UIFont fontWithName:@"Goudy Trajan Regular" size:10]];
    [maskNameLabel setTextColor:[UIColor whiteColor]];//Set text color in label.
    [maskNameLabel setTextAlignment:NSTextAlignmentCenter];//Set text alignment in label.
    [maskNameLabel setBaselineAdjustment:UIBaselineAdjustmentAlignBaselines];//Set line adjustment.
    [maskNameLabel setLineBreakMode:NSLineBreakByCharWrapping];//Set linebreaking mode..
    [maskNameLabel setNumberOfLines:1];//Set number of lines in label.
    //[itemName.layer setCornerRadius:40.0];//Set corner radius of label to change the shape.
    //[itemName.layer setBorderWidth:1.0f];//Set border width of label.
    [maskNameLabel setClipsToBounds:YES];//Set its to YES for Corner radius to work.
    [maskNameLabel.layer setBorderColor:[UIColor blackColor].CGColor];//Set Border color.
    [cell3 addSubview:maskNameLabel];//Add it to the view of your choice.
     
    
    
    return cell3;
    
}



- (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return maskArray.count;
    
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:   (UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    return CGSizeMake(100, 100);
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ //tap brings up delete dialog
    //NSLog(@"Tapped %d", (int)indexPath.row);
    selectedMaskArray = maskArray[indexPath.row]; //assign the new mask to a temporary holding cell
    for (int i = 0; i <= (imagesInCollection.count -1); i++)
    {
        UIImage *tempImage = imagesInCollection[i]; //some technical wizardry to get the correct mask and colour displaying in the individual mask display screen
        //NSLog(@"A: %@", maskName);
        //NSLog(@"B: %@", tempImage.accessibilityIdentifier);
        if([tempImage.accessibilityIdentifier isEqualToString: selectedMaskArray[0]] ){
            outPutMask = tempImage;
        }
    }
    
    [self performSegueWithIdentifier:@"selectedMask" sender:self];
}

Anyone know how to stop this happening? (Objective C answers please, Swift is a bit of a pain to translate to Obj-C sometimes)

enter image description here


Solution

  • You're re-adding maskImage and maskNameLabel again and again when cells are reused. Try to make it as sub-views of collection cell then reassign frame, color, image, text, etc. i.e:

    - (void)viewDidLoad {
        ...
        //Register custom cell with nib
        [_maskGrid registerNib:[UINib nibWithNibName:NSStringFromClass([MaskCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:@"MaskCollectionViewCell"];
    }
    
    - (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
        //Dequeu custom cell
        MaskCollectionViewCell *maskCell = [_maskGrid dequeueReusableCellWithReuseIdentifier:@"MaskCollectionViewCell" forIndexPath:indexPath];
        if (noColourFlag == 0) {
            ...
            maskCell.maskImage.image = maskImage2;
        } else {
            [maskCell.maskImage setImage:[UIImage imageNamed:maskName]];
        }
        //The same with maskNameLabel
    }
    

    Notice: always handle else condition.

    This might be the cell layout:

    enter image description here