reloadRowsAtIndexPaths:withRowAnimation: crashes m

2020-06-07 06:25发布

I got a strange problem with my UITableView: I use reloadRowsAtIndexPaths:withRowAnimation: to reload some specific rows, but the app crashes with an seemingly unrelated exception: NSInternalInconsistencyException - Attempt to delete more rows than exist in section.

My code looks like follows:

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

When I replace that reloadRowsAtIndexPaths:withRowAnimation: message with a simple reloadData, it works perfectly.

Any ideas?

8条回答
叼着烟拽天下
2楼-- · 2020-06-07 06:59

The app crashes because you have made some changes to your tableView. Either you have added or deleted some rows to the tableView. Hence when the view controller asks your model controller class for data, there is a mismatch in the indexPaths. Since the indexPaths have changed after modification.

So either you simply remove the call

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

or replace it with

[self.tableView reloadData];

Calling reloadData checks your number of sections, number of rows in each section and then reloads the whole thing.

查看更多
Viruses.
3楼-- · 2020-06-07 07:00

You should check cell visibility before reload. Here is Swift 3 code:

        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)
        }
查看更多
迷人小祖宗
4楼-- · 2020-06-07 07:02

I had the same issue. In my case; it was happening only if another view controller pop/pushed over existing table view controller and then[self.tableView reloadRowsAtIndexPaths] function is called.

reloadRowsAtIndexPaths call was hiding/showing different rows in a table view which is having over 30, visually complex, rows. As i try to fix the issue i found that if i slightly scroll the table view app wasn't crashing. Also it wasn't crashing if i don't hide a cell (by returning 0 as height)

To resolve the issue, i simply changed the "(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath" function and returned at least 0.01 as row height.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
return rowModel.height + 0.01; // Add 0.01 to work around the crash issue.
}

it solved the issue for me.

查看更多
劳资没心,怎么记你
5楼-- · 2020-06-07 07:06

I had this problem which was being caused by a block calling reloadRowsAtIndexPaths:withRowAnimation: and a parallel thread calling reloadData. The crash was due to reloadRowsAtIndexPaths:withRowAnimation finding an empty table even though I'd sanity checked numberOfRowsInSection & numberOfSections.

I took the attitude that I don't really care if it causes an exception. A visual corruption I could live with as a user of the App than have the whole app crash out.

Here's my solution to this which I'm happy to share and would welcome constructive criticism. If there's a better solution I'm keen to hear it?

- (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;
        }
    });
}
查看更多
Rolldiameter
6楼-- · 2020-06-07 07:18

The problem is that you, probably, have changed size of your table. For example, you have add or remove some source data for table view.

In that case, when you call reloadData your table is completely reloaded including sizes of sections and number of that sections.

But when you call reloadRowsAtIndexPaths:withRowAnimation: that parameters of your table view are not reloaded. That causes next problem: when you are trying to reload some cell table check the size of table view and sees that it has been changed. That results in a crash. This method can be used only when you wan't to reload view of the cell (for example, label has changed or you want to change its size).

Now if you want to remove/add cells from/to table view you should use next approach:

  1. Inform table that its size will be changed by calling method beginUpdates of UITableView
  2. Inform about inserting new row(s) using method - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
  3. Inform about removing row(s) using method - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
  4. Inform table that its size has been changed by calling method endUpdates of UITableView

Hope it will be helpful

查看更多
地球回转人心会变
7楼-- · 2020-06-07 07:22

I think the following code might work:

[self.tableView beginUpdates];

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

[self.tableView endUpdates];
查看更多
登录 后发表回答