Question

In my UITableView Controller I have cells which each contain a UILabel and UISegmentedController. There are about 20 cells in total. The user can select the UISegmentedControl to control whether object is contained in an array or not.

My problem is that as the user scrolls the UISegmentedControl is duplicated and appears as though there are multiple selections for UISegmentedControl. I have a feeling that this is because cells are being reused but unfortunately I don't have a very good understanding of reusing cells.

I have been trying to play around with:

if(cell == nil){}

but I am not sure what should happen there:

Anyway here is my code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"featuresCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *feature = [features objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:9999];
titleLabel.text = feature;

    UISegmentedControl *segmentedControl = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:@"Yes", @"No", nil]];
    segmentedControl.frame = CGRectMake(215, 17, 85, 28);
    [segmentedControl addTarget:self action:@selector(valueChanged:) forControlEvents: UIControlEventValueChanged];
    segmentedControl.tag = indexPath.row;
    [cell addSubview:segmentedControl];

if ([selectedFeatures containsObject:feature]) {
    segmentedControl.selectedSegmentIndex = 0;
}
else{
    segmentedControl.selectedSegmentIndex = 1;
}


return cell;
}

Your help will be greatly appreciated,

Thanks for your time.

Was it helpful?

Solution 3

Try this,

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"featuresCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    NSString *feature = [features objectAtIndex:indexPath.row];
    UILabel *titleLabel = (UILabel *)[cell viewWithTag:9999];
    titleLabel.text = feature;
    UISegmentedControl *segmentedControl;
    if ([cell.contentView viewWithTag:kSegmentTag]) { //kSegmentTag a tag
         segmentedControl = (UISegmentedControl *)[cell viewWithTag:kSegmentTag];
    } else {
         segmentedControl = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:@"Yes", @"No", nil]];
        segmentedControl.frame = CGRectMake(215, 17, 85, 28);
        [segmentedControl addTarget:self action:@selector(valueChanged:) forControlEvents: UIControlEventValueChanged];
        segmentedControl.tag = kSegmentTag;
        [cell.contentView addSubview:segmentedControl];
    }

    if ([selectedFeatures containsObject:feature]) {
        segmentedControl.selectedSegmentIndex = 0;
    }
    else{
        segmentedControl.selectedSegmentIndex = 1;
    }


    return cell;
}

and in valueChanged:

 - (IBAction)valueChanged:(id)sender {
      UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;

      CGPoint tablePoint = [segmentedControl convertPoint:segmentedControl.bounds.origin toView:self.tableView];    

      NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:tablePoint];
      //here indexpath.row give changed row index
 }

OTHER TIPS

cell is never nil so that's not going to help you.

The trick is to mark the cell in some way so as to know whether the segmented control has already been added. The best way is to use the segmented control's tag. Unfortunately you seem to be using the tag for some other purpose, so you'll need to stop doing that:

if (![cell viewWithTag:12345]) {
     UISegmentedControl *segmentedControl = // ....;
     // ....
    segmentedControl.tag = 12345;
    [cell.contentView addSubview:segmentedControl];
}

And now, you see, you are guaranteed that there is exactly one segmented control in the cell, and you can find it by calling [cell viewWithTag:12345].

The general rule is that each cell subclass should implement - (void)prepareForReuse to make it ready for configuration in - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath. I think you can solve your problem by subclassing UITableViewCell, creating each cell with the required UISegmentedControl, and providing a method to configure the segmented control as required.

prepareForReuse should reset the segmented control so that each time you configure your custom cell it is in a known, empty state.

The advantage of this approach is that you are not only reusing the table view cell, but also the segmented control and any other decoration you want the cell to contain. Additionally you have moved the configuration of the cell into the cell object.

Briefly, in the cell subclass init method you create the segmented control and add it as a subview of the contentView.

In the cell subclass configuration method you provide you set the segmented control's segments etc.

In the cell subclass prepareForReuse you remove all those segments and leave the segmented control in a state ready for configuration next time you reuse that cell.

To go even further, start using auto layout to position the segmented control in the cell rather than using a set of hard numbers in a frame. If you get those working on one kind of device that's great but as soon as your cells have different dimensions (in a table view on a different device) things will not look so good.

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