Question

I've been struggling all day to find why my problem occurs.

I have a UICollectionView with cells that contain UIButtons and other UIViews.

My UICollectionView has 8 sections.

I want to modify the image of a UIButton in a specific cell when it is tapped.

I fire a method called "checked" to modify the image.

But instead of modifying only the image of the specific button, it modifies other buttons in other cells. When I press one of these other buttons, they all become "unchecked" again. So, they are linked but I really don't know why!

Here is my code :

- (ExCell*) collectionView:(UICollectionView *)collectView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    //create collection view cell
    ExCell *cell = (ExCell *)[collectView dequeueReusableCellWithReuseIdentifier:@"ExCell" forIndexPath:indexPath];

    // the index is the row number in section + the number of items in all previous sections
    int index = (int)indexPath.row;
    for(int k=0; k < indexPath.section ; k++){
        index += nbItemsInPart[k];
    }
    //configure cell :
    NSMutableString *text = [[NSMutableString alloc]initWithString:@"Practical Exercise "];
    [text appendString:[NSString stringWithFormat:@"%d",index+1]];
    [text appendString:@"\n"];
    [text appendString:[titles objectAtIndex:index]];
    cell.label.text = text;
    [cell.reminder addTarget:self action:@selector(doSomething:) forControlEvents: UIControlEventTouchUpInside];
    [cell.done addTarget:self action:@selector(checked:) forControlEvents:UIControlEventTouchUpInside];
    [cell.doIt addTarget:self action:@selector(doExercise:) forControlEvents: UIControlEventTouchUpInside];
    [cell.doIt setTag:index];
    [cell setTag:indexPath.section];

    // return the cell
    return cell;
}

Here is the method called when the button is tapped (it works but the result affects other additional buttons...) :

-(void)checked:(id)sender{
    if([sender imageForState:UIControlStateNormal] == [UIImage imageNamed:@"checked.png"]){
        [sender setImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
     }else{
         [sender setImage:[UIImage imageNamed:@"checked.png"] forState:UIControlStateNormal];
     }
}

This is the implementation of my custom cell :

@implementation ExCell
@synthesize imageView;
@synthesize label;
@synthesize reminder;
@synthesize done;
@synthesize doIt;

- (id)initWithFrame:(CGRect)aRect
{
    self = [super initWithFrame:aRect];
    {
        CGRect screenBound = [[UIScreen mainScreen] bounds];
        CGSize screenSize = screenBound.size;
        CGFloat screenWidth = screenSize.width;
        CGFloat screenHeight = screenSize.height;

        done = [UIButton buttonWithType:UIButtonTypeCustom];
        done.frame = CGRectMake(0, 0, 85, 112);
        [done setImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
        [self addSubview:done];

        float widthLabel = 0.0f;
        UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
            widthLabel = (screenWidth-6)-158-156-3-85;
        }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
            widthLabel = (screenHeight-24)-158-156-3-85;
        }
        //we create the UIImageView in this overwritten init so that we always have it at hand.
        imageView = [[UIImageView alloc] init];
        imageView.opaque = YES;
        imageView.frame = CGRectMake(86, 0, widthLabel, 112);
        [imageView setBackgroundColor:[UIColor whiteColor]];
        imageView.alpha = 0.7;
        imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
        //set specs and special wants for the imageView here.
        [self addSubview:imageView]; //the only place we want to do this addSubview: is here!

        label = [[UILabel alloc]initWithFrame:CGRectMake(90, 0, widthLabel-5, 112)];
        [label setBackgroundColor:[UIColor clearColor]];
        label.userInteractionEnabled = NO;
        label.numberOfLines = 0;
        [self addSubview:label];

        widthLabel = widthLabel+85+2;

        doIt = [UIButton buttonWithType:UIButtonTypeCustom];
        doIt.frame = CGRectMake(widthLabel, 0, 156, 112);
        doIt.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        [doIt setImage:[UIImage imageNamed:@"faireExo.png"] forState:UIControlStateNormal];
        [self addSubview:doIt];

        widthLabel = widthLabel+1+156;

        reminder = [UIButton buttonWithType:UIButtonTypeCustom];
        reminder.frame = CGRectMake(widthLabel, 0, 158, 112);
        reminder.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        [reminder setImage:[UIImage imageNamed:@"rappel.png"] forState:UIControlStateNormal];
        [self addSubview:reminder];

    }
    return self;
}
@end
Was it helpful?

Solution

Because of cell reuse you can't do it by setting the image in the button's action method. Instead, you should have a mutable array to keep track of the indexPaths of the cells you want to have checked images. You add (or delete) these paths from the array in the button's action method, but you actually set the image in cellForItemAtIndexPath based on what index paths are in the array. This example is for a table view, but the process is the same for a collection view.

@property (strong,nonatomic) NSMutableArray *checkedPaths; // I instantiate this in viewDidLoad

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    RDCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    [cell.button addTarget:self action:@selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
    if ([self.checkedPaths containsObject:indexPath]) {
        [cell.button setImage:[UIImage imageNamed:@"checked.png"] forState:UIControlStateNormal];
    }else{
        [cell.button setImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
    }
    return cell;
}



-(void)customActionPressed:(UIButton *) sender {
    id superView = sender.superview;
    while (superView && ![superView isKindOfClass:[UITableViewCell class]]) {
        superView = [superView superview];
    }// this while loop finds the cell that the button is a subview of


    NSIndexPath *selectedPath = [self.tableView indexPathForCell:(UITableViewCell *)superView];
    if ([self.checkedPaths containsObject:selectedPath]) {
        [self.checkedPaths removeObject:selectedPath];
    }else{
        [self.checkedPaths addObject:selectedPath];
    }

    [self.tableView reloadRowsAtIndexPaths:@[selectedPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // you would use reloadItemsAtIndexPaths: for a collection view
}

OTHER TIPS

That solved the problem! I had to call [collectionview reloadData] though :

-(void)checked:(id)sender{
    UIButton *button = (UIButton *)sender;
    NSNumber *indice = [NSNumber numberWithInt:(int)button.tag];
    if ([self.checkedPaths containsObject:indice]){
        [self.checkedPaths removeObject:indice];
    }else{
        [self.checkedPaths addObject:indice];
    }
    [collectionView reloadData];
}

Thank you so much !!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top