john doe
john doe

Reputation: 9660

Custom CheckBoxView for UITableView Duplicating the Check Marks

I have created a CheckBoxView control for my UITableViewCell. The problem I am facing is that once I checkmark one of the top rows and scrolls the same check mark is visible on the bottom rows. This is because of dequeueresuable rows feature and I want to know how can I fix it. Here is the implementation.

CheckBoxView.m:

-(instancetype) initWithCoder:(NSCoder *)aDecoder {

    self = [super initWithCoder:aDecoder];
    [self setup];
    [self registerGesturesRecognizers];
    return self;
}

-(void) registerGesturesRecognizers {

    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(checkBoxTapped:)];

    [self addGestureRecognizer:tapGestureRecognizer];
}

-(void) checkBoxTapped:(UITapGestureRecognizer *) recognizer {

    if(self.checkBoxViewSelectionChanged) {
        if(!self.isChecked) {
            self.checkBoxViewSelectionChanged(self,self.isChecked);
            self.isChecked = YES;
        }
        else {
            self.checkBoxViewSelectionChanged(self,self.isChecked);

        }
    }
}

-(void) check {

    [_checkBoxImageView setImage:[UIImage imageNamed:@"small-check"]];

}

-(void) uncheck {

    _checkBoxImageView.image = nil;
}

-(void) setup {

    self.userInteractionEnabled = YES;
    self.layer.borderWidth = 0.5f;
    self.layer.borderColor = [UIColor lightGrayColor].CGColor;

    _checkBoxImageView = [[UIImageView alloc] initWithFrame:CGRectMake(1, 0, 23, 23)];
    [self addSubview:_checkBoxImageView];
}

And here is the cellForRowAtIndexPath method:

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

    SamplesTableViewCell *cell = (SamplesTableViewCell *) [tableView dequeueReusableCellWithIdentifier:@"SamplesTableViewCell" forIndexPath:indexPath];

    Item *sample = [_samples objectAtIndex:[indexPath row]];

    cell.productNameLabel.text = sample.product.name;
    cell.productColorLabel.text = sample.productColor.name;
    [cell.productImageView setImage:sample.productColor.image];

    cell.checkboxView.checkBoxViewSelectionChanged = ^(CheckBoxView *checkBoxView, BOOL isChecked) {

        if(!isChecked) {
            [checkBoxView check];
            checkBoxView.isChecked = YES;
        }
        else {
            [checkBoxView uncheck];
            checkBoxView.isChecked = NO;
        }

    };

    return cell;
}

The CheckBoxView is actually a UIView on the Storyboard prototype cell whose class is set to CheckBoxView so it is not created dynamically in the cellForRowAtIndexPath event. When I run the above code and checkmark the top rows and scroll then the same checkmark appears on the lower rows.

UPDATE:

Here is my updated code but it still checks and unchecks the rows at the bottom.

  cell.checkboxView.checkBoxViewSelectionChanged = ^{

        if(!sample.isSelected) {

            [sample setSelected:YES];
            [cell.checkboxView check];
        }
        else
        {
            [sample setSelected:NO];
            [cell.checkboxView uncheck];
        }


    };

Upvotes: 0

Views: 63

Answers (1)

Tim
Tim

Reputation: 9042

Your UIView should not be maintaining the isChecked state for your checkbox. As you pointed out, due to cell reuse all the others will then be checked because the same UIView is being used.

Your model objects powering your data need to maintain the state, or your controller.

For example:

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

   SamplesTableViewCell *cell = (SamplesTableViewCell *) [tableView dequeueReusableCellWithIdentifier:@"SamplesTableViewCell" forIndexPath:indexPath];

   // Some code...

   if ([self.myDatasource[indexPath.row] isChecked]) {
       cell.checkBoxView.isChecked = YES;
   }

   return cell;
}

Just some rough pseudocode, but hopefully you get the idea. Potential ways to implement this state maintained is either in the form of model objects, or in your UIViewController have an NSArray containing an NSDictionary representing each row in your UITableView, where each key-value pair maintains a particular state for your UITableViewCell.

Upvotes: 3

Related Questions