UITableView 中同时插入/删除行的问题

Posted

技术标签:

【中文标题】UITableView 中同时插入/删除行的问题【英文标题】:Problems with simultaneous insertion/deletion of rows in UITableView 【发布时间】:2011-11-22 16:27:13 【问题描述】:

这几天我一直在努力解决这个问题。我需要在 beginUpdates/endUpdates 块内的 UITableView 中同时删除和插入一些行,但是一些插入的行的索引超过了原始表视图元素计数。我从Apple文档中获取有关批量插入和删除的示例代码并对其进行了修改以引起同样的问题,代码如下:

#import "MasterViewController.h"

@implementation MasterViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) 
        self.title = NSLocalizedString(@"Master", @"Master");
        states = [[NSMutableArray arrayWithObjects:@"Arizona", @"California", @"New Jersey", @"Washington", nil] retain];
    
    return self;


- (void)dealloc

    [states release];
    [super dealloc];


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    return [states count];


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    

    cell.textLabel.text = [states objectAtIndex:indexPath.row];
    return cell;


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    [states removeObjectAtIndex:0];
    [states removeObjectAtIndex:0];
    [states removeObjectAtIndex:0];

    [states insertObject:@"Alaska" atIndex:1];
    [states insertObject:@"Georgia" atIndex:1];
    [states insertObject:@"Wisconsin" atIndex:1];

    NSArray *deleteIndexPaths = [NSArray arrayWithObjects:
                             [NSIndexPath indexPathForRow:0 inSection:0],
                             [NSIndexPath indexPathForRow:1 inSection:0],
                             [NSIndexPath indexPathForRow:2 inSection:0],
                             nil];

    NSArray *insertIndexPaths = [NSArray arrayWithObjects:
                             [NSIndexPath indexPathForRow:2 inSection:0],
                             [NSIndexPath indexPathForRow:3 inSection:0],
                             [NSIndexPath indexPathForRow:4 inSection:0],
                             nil];

    UITableView *tv = (UITableView *)self.view;

    [tv beginUpdates];
    [tv insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];
    [tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationTop];
    [tv endUpdates];


@end

当我运行它并点击任何行时,我得到以下异常:

*** Assertion failure in -[_UITableViewUpdateSupport _computeRowUpdates], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableViewSupport.m:386
Uncaught exception: 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.

这段代码有什么我没有看到的根本错误吗?

【问题讨论】:

既然你本质上是在替换行,为什么不重新加载那些索引路径而不是删除和重新插入?...只是一个虽然 因为这是一个更简单的例子。在我的真实应用中,删除和插入的行数并不总是相同的。 【参考方案1】:

insertRowsAtIndexPaths:withRowAnimation: 的文档明确指出,删除发生在插入更新块之前:

注意在 beginUpdates 和 endUpdates 方法定义的动画块中调用此方法时的行为。 UITableView 将任何行或节的插入推迟到它处理了行或节的删除之后。无论插入和删除方法调用的顺序如何,都会发生这种情况。

Table View 编程指南中的更多详细信息:

但是,这不是 UITableView 完成操作的顺序。它将任何行或节的插入推迟到它处理了行或节的删除之后。表格视图的行为方式与在更新块内调用的重新加载方法相同——在执行动画块之前,重新加载是针对行和节的索引进行的。无论插入、删除和重新加载方法调用的顺序如何,都会发生此行为。

动画块中的删除和重新加载操作指定原始表中的哪些行和部分应该被删除或重新加载;插入指定应将哪些行和部分添加到结果表中。用于标识节和行的索引路径遵循此模型。另一方面,在可变数组中插入或删除项目可能会影响用于连续插入或删除操作的数组索引;例如,如果您在某个索引处插入一个项目,则数组中所有后续项目的索引都会递增。

【讨论】:

是的,我读到了。但它也指出索引(索引?)应该在开始/结束更新之前与表状态相关,这就是我正在做的......不是吗? 好吧,删除应该在任何更新之前使用 indexPaths,插入应该在应用所有删除之后使用 indexPaths。至少我是这么读的。 没关系,我看错了第二段,插入索引路径应该在删除之后考虑。对此感到抱歉【参考方案2】:

在这种情况下,您不需要插入或删除行,从技术上讲,totalAddedRows = addedRows-deleteRows(此处为 3-3=0),因此只需更新行或重新加载表数据会自动更改。

如果 totalAddedRows 为 +ve,则插入新行,如果为负数,则删除行。不要同时添加和删除。

注意:您的应用程序会崩溃,因为如果您调用 insert Row,那么总数组计数仅为 3。

【讨论】:

以上是关于UITableView 中同时插入/删除行的问题的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 在保留已经获取的行的同时插入更多行

使用“插入”行初始化 UITableView

如何在 UITableView 中插入和删除行时模拟折纸

获取将在 UITableView 中插入的行的索引

删除和插入具有不同高度的单元格时,UITableView 动画故障

如何启用移动但禁用 UITableView 中某些行的删除