reloadRowsAtIndexPaths:withRowAnimation:导致我的应用程序崩溃

22

我的UITableView出现了一个奇怪的问题:我使用reloadRowsAtIndexPaths:withRowAnimation:来重新加载一些特定的行,但是应用程序崩溃并显示一个看似无关的异常: NSInternalInconsistencyException - 尝试删除超过该分区中存在的行数。

我的代码如下:

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

当我将reloadRowsAtIndexPaths:withRowAnimation:消息替换为简单的reloadData时,它可以完美地工作。

有什么想法吗?


1
请查看类似的问题:http://stackoverflow.com/questions/3542260/how-can-i-increase-the-height-of-uitableviewcell-dynamiclly/3542289,也许会有帮助。 - Vladimir
9个回答

32
问题在于您可能更改了UITableView数据源的项目数量。 例如,您已经从/向实现UITableViewDataSource协议中使用的数组或字典中添加或删除了一些元素。
在这种情况下,当您调用reloadData时,您的UITableView将完全重新加载,包括部分和行数。
但是,当您调用reloadRowsAtIndexPaths:withRowAnimation:时,这些参数不会被重新加载。这导致下一个问题:当您尝试重新加载某个单元格时,UITableView检查数据源的大小并看到它已更改,从而导致崩溃。只有在要重新加载单元格的内容视图(例如标签已更改或要更改其大小)时才可以使用此方法。
现在,如果您想从/向UITableView中删除/添加单元格,则应使用以下方法:
  1. 通过调用方法beginUpdates通知UITableView其大小将被更改。
  2. 使用方法 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 通知插入新行。
  3. 使用方法-(void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 通知删除行。
  4. 通过调用方法endUpdates通知UITableView其大小已更改。

理论上讲是有道理的,但实际上并不起作用。我正在尝试重新加载某些部分,但它仍然崩溃。我在使用UITableViewAutomaticDimension来估算表头和单元格高度,这是一个要求。 - sheetal
@sheetal,如果你采用这个答案中的方法,就不必调用reloadRowsAtIndexPaths:withRowAnimation:。基本上,只需将一行添加到您的模型数据结构(在大多数情况下是一个数组)中。然后调用beginUpdate、insertRowsAtIndexPaths和endUpdates。 - RajV
我正在预取图片,当已预取的图片加载完成后需要更新行。例如,如果用户滚动到底部并加载了更多帖子到Feed中,则这会导致崩溃。我不明白如何可能避免崩溃-开始/结束更新无法工作,在主线程上运行所有修改函数也无效。我陷入困境。 - JCutting8

9
我认为以下代码可能有效:
```html

我认为以下代码可能有效:

```
[self.tableView beginUpdates];

[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

[self.tableView endUpdates];

这个方法解决了我的问题。我正在使用核心数据,我已经传递了一个不同的获取请求到结果获取控制器,并在获取完成后重新加载表格。即使获取成功,应用程序也会崩溃。但是使用上述方法,重新加载是成功的。 - prajul
不正确。如果只有一个reloadRowsAtIndexPath,则不需要使用beginUpdates/endUpdates,但如果这是尝试重新加载新添加的行,则永远无法修复崩溃。有关详细信息,请查看Nekto的答案。 - Vitalii
对我来说不起作用。我也不明白为什么需要调用begin/end updates,因为你没有使用reloadRowsAtIndexPaths修改表视图中的行数。 - JCutting8

4

我的问题是由一个调用reloadRowsAtIndexPaths:withRowAnimation:的块和一个并行线程调用reloadData引起的。尽管我进行了numberOfRowsInSection和numberOfSections的健全性检查,但是reloadRowsAtIndexPaths:withRowAnimation仍然找到了一个空表格,并导致了崩溃。

我认为如果它引发异常,那也没关系。作为应用程序的用户,我可以容忍视觉上的损坏,而不是整个应用程序崩溃。

下面是我的解决方案,我很乐意分享并欢迎建设性的批评。如果有更好的解决方案,我很愿意听取?

- (void) safeCellUpdate: (NSUInteger) section withRow : (NSUInteger) row {
    // It's important to invoke reloadRowsAtIndexPaths implementation on main thread, as it wont work on non-UI thread
    dispatch_async(dispatch_get_main_queue(), ^{
        NSUInteger lastSection = [self.tableView numberOfSections];
        if (lastSection == 0) {
            return;
        }
        lastSection -= 1;
        if (section > lastSection) {
            return;
        }
        NSUInteger lastRowNumber = [self.tableView numberOfRowsInSection:section];
        if (lastRowNumber == 0) {
            return;
        }
        lastRowNumber -= 1;
        if (row > lastRowNumber) {
            return;
        }
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
        @try {
            if ([[self.tableView indexPathsForVisibleRows] indexOfObject:indexPath] == NSNotFound) {
                // Cells not visible can be ignored
                return;
            }
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        }

        @catch ( NSException *e ) {
            // Don't really care if it doesn't work.
            // It's just to refresh the view and if an exception occurs it's most likely that that is what's happening in parallel.
            // Nothing needs done
            return;
        }
    });
}

在Swift中,do-try-catch表示块中没有函数抛出任何异常,因此永远不会到达catch语句。 - JCutting8

2

经过多次尝试,我发现"reloadRowsAtIndexPaths"只能在某些地方使用,如果只更改单元格内容而不是插入或删除单元格。并非所有地方都可以使用它,即使您将其包装在中也不行。

[self beginUpdates];
//reloadRowsAtIndexPaths
[self endUpdates];

我发现可以使用它的地方有:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath - (IBAction) unwindToMealList: (UIStoryboardSegue *) sender

尝试从其他地方调用,如“viewDidLoad”或“viewDidAppear”,都不会生效(我的意思是对于已经加载的单元格,重新加载将不会生效),或者会导致异常。

因此,请仅在这些地方使用“reloadRowsAtIndexPaths”。


2

在重新加载之前,您应该检查单元格的可见性。以下是适用于Swift 3的代码:

        let indexPath = IndexPath(row: offset, section: 0)
        let isVisible = tableView.indexPathsForVisibleRows?.contains{$0 == indexPath}
        if let v = isVisible, v == true {
            tableView.reloadRows(at: [indexPath], with: .automatic)
        }

1
我曾经遇到过完全相同的问题,解决方法是:如果indexPath不可见,则使用reloadData - zaltzy

1
我遇到了同样的问题。在我的情况下,只有在另一个视图控制器弹出/推入现有表视图控制器并且调用[self.tableView reloadRowsAtIndexPaths]函数时才会发生。
reloadRowsAtIndexPaths调用会隐藏/显示表视图中的不同行,该表视图具有超过30个视觉上复杂的行。当我试图解决这个问题时,我发现如果我轻微滚动表格视图,应用程序就不会崩溃。如果我不隐藏单元格(通过返回0作为高度),它也不会崩溃。
为了解决这个问题,我只需更改"(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath"函数,并返回至少0.01作为行高即可。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
return rowModel.height + 0.01; // Add 0.01 to work around the crash issue.
}

它解决了我的问题。

1

这是旧的,请勿使用。 当我调用reloadRowsAtIndexPaths...以将单元格更改为包含UITextField的编辑单元格时,我遇到了此问题。错误告诉我正在删除表中的所有行。为解决问题,我删除了:

[self.tableView beginUpdates];
NSArray *reloadIndexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:count inSection:section]];
[self.tableView reloadRowsAtIndexPaths:reloadIndexPath withRowAnimation:UITableViewRowAnimationFade];  
[self.tableView endUpdates];

并将其替换为

[self.tableView reloadData];

0
如果数据计数完全改变,则使用reloadData,否则有三个函数可以完成它。
当数据计数更改时,我们使用insertRows / deleteRows,而当数据计数仍然相同时,使用reloadRows
重要!不要忘记在insertRows/deleteRows/reloadRows调用之间调用beginUpdatesendUpdates

0
应用程序崩溃是因为您对tableView进行了一些更改。您可能已经添加或删除了tableView的某些行。因此,当视图控制器向您的模型控制器类请求数据时,indexPaths存在不匹配的情况。由于修改后indexPaths已更改。
因此,您可以简单地删除调用。
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

或者替换它

[self.tableView reloadData];

调用reloadData会检查你的每个部分中的行数和部分数量,然后重新加载整个内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接