使用UISwitch显示和隐藏UITableViewCell过快崩溃

时间:2012-02-07 09:53:54

标签: ios uitableview crash uiswitch

我有一个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而不是动画也解决了问题,但我更喜欢漂亮的动画。

有人知道实现这个的正确方法吗?

5 个答案:

答案 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];