我有一个UITableView,它向用户显示一些设置。除非UISwitch处于“开”位置,否则某些单元格将被隐藏。我有以下代码:
- (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];
}
这可以正常工作,直到我快速连续两次切换UISwitch(通过双击),在这种情况下应用程序崩溃
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.
我知道它是由numberOfRowsInSection的错误返回值引起的,因为当单元格动画仍在播放时,开关回到其原始位置。我已经尝试禁用切换并在其他事件处理程序下挂钩代码,但似乎没有任何东西可以防止崩溃。使用reloadData而不是动画也解决了问题,但我更喜欢漂亮的动画。
有人知道实现这个的正确方法吗?
答案 0 :(得分:3)
问题的另一个(更优雅的)解决方案是:
我用这种方式修改了Alan MacGregor - (IBAction)SwitchDidChange:(id)sender
方法:
- (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];
}
}
其他部分保持不变。
答案 1 :(得分:2)
只需将交换机的enabled
属性设置为NO
,直到更新完成。
答案 2 :(得分:2)
我遇到了这个问题,避免崩溃的方法是不明确使用uiswitch,而是将信息转换为布尔值,以及我是如何做到的。
在实现文件的顶部添加一个布尔值
bool _showRows = NO;
更新您的uiswitch代码
- (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];
}
}
最后更新你的numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
if (_showRows) {
return 5;
}
else {
return 1;
}
}
return 0;
}
答案 3 :(得分:0)
即使控件的值实际上没有更改,也会发生UIControlEventValueChanged事件。因此即使交换机的值没有改变,也会调用togglePush。当您快速切换开关时,您可能并不总是从&gt;开始。关&gt;在&gt;关闭,等等可以关闭&gt;在&gt;在&gt;关闭。
所以正在发生的事情是你连续两次造成两个insertSections一个接一个。这显然很糟糕。
要解决此问题,您需要记住按钮的先前状态(可能是ivar),并且只有在新值(source.on)与先前值不同时才执行插入(或删除)
答案 4 :(得分:0)
所以我也遇到了这个问题。您必须在更改值后立即禁用 UISwitch。然后您在 performBatchUpdates
(iOS 11 及更高版本!)内进行更新。更新完成后,您将在回调中启用 UISwitch。
//...method with value change or other control event:
yourUISwitch.enabled = NO;
//...method with your tableview inserts or deletes:
[self.tableView beginUpdates];
[self.tableView performBatchUpdates:^{
[self.tableView insertRowsAtIndexPaths:yourIndexPath withRowAnimation:UITableViewRowAnimationFade];
} completion:^(BOOL finished) {
if(finished){
yourUISwitch.enabled = YES;
}
}];
[self.tableView endUpdates];