Oleksandr Matrosov
Oleksandr Matrosov

Reputation: 27133

UITableView insert rows crash

I have this delegate method that return for me data from API:

- (void)pagedDataSource:(PagedDataSource *)dataSource
 didLoadAdditionalItems:(NSInteger)items; {


    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:items];
    NSInteger numberOfItems = [dataSource numberOfItems];

    for (int i = (int)numberOfItems - (int)items; i < numberOfItems; ++i) {
        [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
    }

    [UIView setAnimationsEnabled:NO];
    [[self tableView] beginUpdates];

    [[self tableView] insertRowsAtIndexPaths:indexPaths
                            withRowAnimation:UITableViewRowAnimationNone];

    [[self tableView] endUpdates];
    [UIView setAnimationsEnabled:YES];
}

So first time it works good but when I try to load new entries and then program invokes this delegate method again, it causes a crash.

Let me simple describe how it works.

So I have PagedDataSource object with I initialize with some block that should request data in return when PagedDataSource received data from API it push up data to my view controller with self pointer. So you can see this method above. Then in this method I try to insert needed rows that I just receive. When I scroll table down it request new items and I insert one more rows each time when app invokes this method after receive new data.

So for example if I receive 16 items first time so then I insert rows from 0 to 15

Then when I scroll down table I insert new rows from 16 to 31. So let's say that my limit = 16 items per request.

But the problem that after I load some item for example 144 items, I want to reload my table with new data set. So I starting load data from 0.

Let's say that first time I requested 144 videos then I tap a button and right now I want to see 7 videos from another category in this table. But it cause this crash, because here

- (void)pagedDataSource:(PagedDataSource *)dataSource
     didLoadAdditionalItems:(NSInteger)items;

it tries to insert new items from 0 and I got this issue:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 144 into section 0, but there are only 0 rows in section 0 after the update'

So I checked count for indexPaths array, and here is just 7 items first time.

What should I do? How I can remove or free table view?

So just skip all write before. So let's suppose I load 16 items first time

this is log of indexPaths array I described above:

<__NSArrayM 0x7fbdc1e8af50>(
<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0},
<NSIndexPath: 0xc000000000008016> {length = 2, path = 0 - 1},
<NSIndexPath: 0xc000000000010016> {length = 2, path = 0 - 2},
<NSIndexPath: 0xc000000000018016> {length = 2, path = 0 - 3},
<NSIndexPath: 0xc000000000020016> {length = 2, path = 0 - 4},
<NSIndexPath: 0xc000000000028016> {length = 2, path = 0 - 5},
<NSIndexPath: 0xc000000000030016> {length = 2, path = 0 - 6},
<NSIndexPath: 0xc000000000038016> {length = 2, path = 0 - 7},
<NSIndexPath: 0xc000000000040016> {length = 2, path = 0 - 8},
<NSIndexPath: 0xc000000000048016> {length = 2, path = 0 - 9},
<NSIndexPath: 0xc000000000050016> {length = 2, path = 0 - 10},
<NSIndexPath: 0xc000000000058016> {length = 2, path = 0 - 11},
<NSIndexPath: 0xc000000000060016> {length = 2, path = 0 - 12},
<NSIndexPath: 0xc000000000068016> {length = 2, path = 0 - 13},
<NSIndexPath: 0xc000000000070016> {length = 2, path = 0 - 14},
<NSIndexPath: 0xc000000000078016> {length = 2, path = 0 - 15}
)

then I have switcher that load in the same table another objects and then call the method I described above here is a log of indexPaths (second time):

<__NSArrayM 0x7fbdc1e13920>(
<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0},
<NSIndexPath: 0xc000000000008016> {length = 2, path = 0 - 1},
<NSIndexPath: 0xc000000000010016> {length = 2, path = 0 - 2},
<NSIndexPath: 0xc000000000018016> {length = 2, path = 0 - 3},
<NSIndexPath: 0xc000000000020016> {length = 2, path = 0 - 4},
<NSIndexPath: 0xc000000000028016> {length = 2, path = 0 - 5},
<NSIndexPath: 0xc000000000030016> {length = 2, path = 0 - 6}
)

I got SIGABRT on [[self tableView] endUpdates]; line

Upvotes: 1

Views: 2812

Answers (1)

0yeoj
0yeoj

Reputation: 4550

I think you error is when appending the NSIndexPath specifically in indexPathForRow:i. And make sure that your dataSource is updated.

Try this:


[dataSource addObject:@"boom0"];
[dataSource addObject:@"boom1"];
[dataSource addObject:@"boom3"];
[dataSource addObject:@"boom4"];

NSInteger item = 4;

NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:item];

for (int i = item; i != 0; i--)
{
    [indexPaths addObject:[NSIndexPath indexPathForRow:dataSource.count - i inSection:0]];
}

[tableView beginUpdates];

[tableView insertRowsAtIndexPaths:indexPaths
                        withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

EDIT

To repopulate the table what you need to do is to removeAll the object from you dataSource like:

[dataSource removeAllObjects];

the reload your table

[tableView reloadData];

the trick is all in your dataSource it is up to you how to manipulate it.


//This is using method

- (void)pagedDataSource:(PagedDataSource *)dataSource didLoadAdditionalItems:(NSInteger)items repopulate:(BOOL)repopulate
{
    if(repopulate)
    {
        [dataSource removeAllObjects];

        [tableView reloadData];
    }

    //this will execute the codes below 

    ...
}

You can also track the number of rows in section: If the current count of dataSource is less than the previous count

trigger the removeAllObjects & reloadData:

int previousCount = 0;

- (void)pagedDataSource:(PagedDataSource *)dataSource didLoadAdditionalItems:(NSInteger)items
{
    if([dataSource numberOfItems] < previousCount)
    {
        [dataSource removeAllObjects];

        [tableView reloadData];
    }

    //this will execute the codes below 

    ...
}

Another option is to just reload the table while the dataSource was already update, since the cell was already made we don't need to create a new one, or perhaps you like to remake cells? hmm..

- (void)pagedDataSource:(PagedDataSource *)dataSource didLoadAdditionalItems:(NSInteger)items
{
    if([dataSource numberOfItems] < previousCount) // || if(repopulate)
    {
        [tableView reloadData];

        return;
    }

    //this will not / must not execute the codes below 

    ...
}

Hope this helps you, Cheers.. :)

Upvotes: 1

Related Questions