Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Showing and hiding UITableViewCell with UISwitch too fast crashes

I've got a UITableView that presents some settings to the user. Some cells are hidden unless a UISwitch is in the 'On' position. I've got the following code:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return switchPush.on ? 6 : 1;
}

// Hooked to the 'Value Changed' action of the switchPush
- (IBAction)togglePush:(id)sender {
    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:0];
    for(int i = 1; i <= 5; i++) {
        [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
    }

   [tableView beginUpdates];
    if(switchPush.on) {
        [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
    } else {
        [tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
    }
   [tableView endUpdates];
}

This works as expected, until I switch the UISwitch twice in rapid succession (by double tapping), in which case the application crashes with a

Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source.

I know that it is caused by the wrong return value of numberOfRowsInSection as the switch is back in its original position while the cell animation is still playing. I've tried disabling the toggle and hooking the code under other event handlers but nothing seems to prevent the crash. Using reloadData instead of the animation also solves to problem but I would prefer the nice animations.

Does anybody know a correct way of implementing this?

like image 901
Paul Wagener Avatar asked Feb 07 '12 09:02

Paul Wagener


3 Answers

Another (more elegant) solution at the problem is this:

I modified the Alan MacGregor - (IBAction)SwitchDidChange:(id)sender method in this way:

- (IBAction)SwitchDidChange:(UISwitch *)source {
     if (_showRows != source.on) {
         NSArray *aryTemp = [[NSArray alloc] initWithObjects:
                    [NSIndexPath indexPathForRow:1 inSection:0],
                    [NSIndexPath indexPathForRow:2 inSection:0],
                    [NSIndexPath indexPathForRow:3 inSection:0],
                    [NSIndexPath indexPathForRow:4 inSection:0],nil];
         [_tblView beginUpdates];
         _showRows = source.on;
         if (_showRows) {
             [_tblView insertSections:aryTemp withRowAnimation:UITableViewRowAnimationFade];
         }
         else {
             [_tblView deleteSections:aryTemp withRowAnimation:UITableViewRowAnimationFade];
         }
         [_tblView endUpdates];
     }
}

The other parts stay unchanged.

like image 130
Giorgio Avatar answered Nov 15 '22 00:11

Giorgio


Simply set the enabled property of your switch to NO until the updates are done.

like image 36
Mundi Avatar answered Nov 15 '22 00:11

Mundi


I had this issue on mine and the way to avoid the crash is to not explicitly use the uiswitch, instead relay the information into a boolean, heres how I did it.

Add a boolean to the top of your implementation file

bool _showRows = NO;

Update your uiswitch code

- (IBAction)SwitchDidChange:(id)sender {

NSArray *aryTemp = [[NSArray alloc] initWithObjects:[NSIndexPath indexPathForRow:1 inSection:0],
                    [NSIndexPath indexPathForRow:2 inSection:0],
                    [NSIndexPath indexPathForRow:3 inSection:0],
                    [NSIndexPath indexPathForRow:4 inSection:0],nil];

if (_showRows) {
    _showRows = NO;
    _switch.on = NO;
    [_tblView  deleteRowsAtIndexPaths:aryTemp withRowAnimation:UITableViewRowAnimationTop];
}
else {
    _showRows = YES;
    _switch.on = YES;
    [_tblView insertRowsAtIndexPaths:aryTemp withRowAnimation:UITableViewRowAnimationBottom];
}
}

And finally update your numberOfRowsInSection

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {

    if (_showRows) {
        return 5;
    }
    else {
        return 1;

    }      
}
return 0;  
}
like image 24
Alan MacGregor Avatar answered Nov 15 '22 00:11

Alan MacGregor