Search code examples
iosuisegmentedcontrolios13

iOS 13 UISegmentedControl inside UITableView caches previous selection


I have a screen with UITableView which displays one row per data element available, count of data element can be in hundreds. Depending on the data element type, each rows shows either an input field or segmented control. There is problem with the segmented control in iOS 13, when user taps first or second segment of a segmented control row on the top of the view and scrolls down, it auto select first segment for some random segmented control row in the subsequent scrolling view/page. It looks as if the control state is not refreshed.

Here is the code snippet of cellForRowAtIndexPath function where it creates the UISegmentedControl row.

QuestionOnTopTableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"YesNoInputIdentifier"];
     if (!cell) {
        cell = [[QuestionOnTopTableViewCell alloc] initWithCellStyle:QuestionCellStyleYesNo reuseIdentifier:@"YesNoInputIdentifier"];
     }
     cell.titleLabel.text = row.title;
     cell.segmentControl.selectedSegmentIndex = row.answer ? row.answer.boolValue : UISegmentedControlNoSegment;

Any one ran into this issue?

Note: This issue happens only on iOS13.


Solution

  • The system will only allocate enough cells to visually display, and then when one scrolls off it reuses it for the new cell scrolling in. As a result - when this happens it remembers the previous selection as you're seeing.

    Not sure why this wasn't happening pre iOS 13 (it does look like your cellForRowAt: function should set the correct value if you're looking at the right row variable, but you should just be able to reset it by overriding the UITableViewCells prepareForReuse() method.

    Swift

    class QuestionOnTopTableViewCell: UITableViewCell {
    
         // stuffs
    
        override func prepareForReuse() {
            super.prepareForReuse()
            self.segmentControl.selectedSegmentIndex = .noSegment
        }
    }
    

    Obj-C

    This should work for you:

    -(void)prepareForReuse{
        [super prepareForReuse];
        self.segmentControl.selectedSegmentIndex = UISegmentedControlNoSegment;
    }
    

    Edit:

    I actually did just see another question claiming .noSegment doesn't work anymore after a value has been set on iOS13/Xcode 11 builds: UISegmentedControl.noSegment stopped working with Xcode 11, iOS 13

    What you may need to do instead, unfortunately, is recreate a local version of the segmented control every time and overriding the control: cell.segmentControl = myNewJustCreatedSegmentControl

    you can perhaps still do that in the prepareForReuse method