1

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.

shim
  • 9,289
  • 12
  • 69
  • 108
Shayno
  • 788
  • 9
  • 26
  • wrap your code from `NSString *feature` to rest inside like, `if(cell == nil) { NSString *feature = ........} return cell;` – arthankamal Mar 17 '14 at 04:19
  • Are you using storyboard? – Akhilrajtr Mar 17 '14 at 04:21
  • @arthan.v Thanks but if I do that than the segment is not displayed and the text of the label does not change to show the feature. – Shayno Mar 17 '14 at 04:23
  • @Akhilrajtr Yes I am using storyboard, but I am not adding the UISegmentedControl in storyboard, I am add it in cellForRow so I can tell which feature is being added/removed by the tag value. – Shayno Mar 17 '14 at 04:26
  • 3
    Never added any subview to the cell! Add the subview to the cell's `contentView`. – matt Mar 17 '14 at 04:27

3 Answers3

1

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].

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks Matt your answer is correct also but I mark the other one as the got in first. Thanks for the advice with `cell.ContentView addSubview` also! – Shayno Mar 17 '14 at 05:13
1

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.

Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114
0

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
 }
Akhilrajtr
  • 5,170
  • 3
  • 19
  • 30
  • Thanks but if I use this code I get an error `'NSInvalidArgumentException', reason: '-[UITableViewCell setSelectedSegmentIndex:]`. I was getting this error earlier also and I can't understand why – Shayno Mar 17 '14 at 04:39
  • @Shayno use `kSegmentTag` as constant tag for segmentcontrol and try. – Akhilrajtr Mar 17 '14 at 04:41
  • Sorry I am not familiar with what kSegmentTag is... I get undeclared Identifier when adding it. – Shayno Mar 17 '14 at 04:43
  • @Shayno you can see it [here](http://stackoverflow.com/questions/6188672/where-do-you-declare-constant-in-objective-c) or just use 8888 or other value instead – Akhilrajtr Mar 17 '14 at 04:45
  • Okay I have got it working fine now, just one thing I needed to change in the code. With `segmentedControl = (UISegmentedControl *)[cell viewWithTag:indexPath.row];`. The code was finding other objects with tag `indexPath.Row` and returning an error when the objects selectedSegmentIndex couldn't be changed. So I just changed it to `segmentedControl = (UISegmentedControl *)[cell viewWithTag:8888];`. – Shayno Mar 17 '14 at 05:11
  • Thanks for the awesome answer. I found it extremely helpful. – Shayno Mar 17 '14 at 05:12
  • @Shayno I forgot to change that one. thanks for pointing it. Happy to know it helped you. Cheers. – Akhilrajtr Mar 17 '14 at 05:13
  • You're doing it wrong. Using viewWithTag: here is an ugly hack - you should use a subclass of UITableViewCell to manage its own segmented control and title label. – Adam Eberbach Mar 17 '14 at 23:36
  • When using this solution, anytime I select a segmentedControl item, it refuses to 'deselect'. How can I 'refresh' the cell? I'm using [tableView reload] – Jason McDermott Dec 21 '15 at 04:40