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?
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.
Simply set the enabled
property of your switch to NO
until the updates are done.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With