views:

1616

answers:

2

I have a SQLite database tied to my uitableview. There are about 2000 rows, so I retrieve 25 rows at first. Once I scroll to the end of the list, I want it to automatically retrieve 25 more rows.

Here's the method I'm using now. Ignore what would happen if I load all 2000 rows, I'll worry about that later. In tableView:numberOfRowsInSection: I return one more than the currently loaded rows. Then in tableView:cellForRowAtIndexPath: I load more rows if it loads the last row.

Typically I can get down a few pages, but I eventually get an exception: *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (10) beyond bounds (10)'. The index is always 8-10. I don't know what array it is, but both my rows and indices array have well more than 10 items.

tableView:cellForRowAtIndexPath:

if(indexPath.row >= [rows count]) {
    [cell.textLabel setText:@"Loading..."];
 if(!updating && indexPath.row == [rows count]) {
  updating = YES;
  [[self tableView] beginUpdates];
  NSMutableArray *indices = [NSMutableArray array];
  // stmt = ...
  while(sqlite3_step(stmt) == SQLITE_ROW) {
      [rows addObject:@"..."];
      [indices addObject:[NSIndexPath indexPathForRow:[rows count]-1 inSection:0]];
     }
  [[self tableView] insertRowsAtIndexPaths:indices withRowAnimation:UITableViewRowAnimationNone];
  [[self tableView] endUpdates];
  updating = NO;
 }
 return cell;
}

Stack trace:

#0  0x01ce4004 in ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ ()
#1  0x9779df49 in objc_exception_throw ()
#2  0x01cc5c3b in +[NSException raise:format:arguments:] ()
#3  0x01cc5b9a in +[NSException raise:format:] ()
#4  0x00072cc9 in _NSArrayRaiseBoundException ()
#5  0x00010227 in -[NSCFArray objectAtIndex:] ()
#6  0x0047fe5b in -[_UITableViewUpdateSupport(Private) _setupAnimationsForExistingVisibleCells] ()
#7  0x0047f9e0 in -[_UITableViewUpdateSupport initWithTableView:updateItems:oldRowData:newRowData:oldRowRange:newRowRange:context:] ()
#8  0x002eca2b in -[UITableView(_UITableViewPrivate) _updateWithItems:withOldRowData:oldRowRange:newRowRange:context:] ()
#9  0x002ec757 in -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] ()
#10 0x002def56 in -[UITableView endUpdates] ()
+3  A: 

I'm a little unclear why you're inserting rows. It looks to me that you're making this quite a bit more complicated than it needs to be. UITableView will only ask you for values for table cells that are on screen (or soon will be). What's more, by continually adding rows to the table view, the scroll bar doesn't reflect the user's actual position in the data set.

It would make just as much sense to return the total number of rows in tableView:numberOfRowsInSection: (you can use the SQLite COUNT aggregate function to do this without actually fetching all the data) and to cache additional data as needed (using something similar to what you have above) when you experience a "cache miss".

If I had to guess why this code is causing you problems, though, it would be that you're modifying the table view's contents while the table view is waiting for you to return a cell.

I would advise something that looks a little bit more like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath row] >= [cache count]) {
        // Load additional items into the cache
    }

    // Do the regular cell setup stuff here; you can assume the data you need is available
    return cell;
}
Alex
Thats a great solution! Thanks a lot!
Markus
A: 

Alex, I'm accepting your answer, although I'm still interested in what is causing the actual exception.

Your comment on updating the tableview while returning a cell led me to implement - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath to load more rows when needed, which is more stable, although I have to do so in a separate thread to get any kind of performance.

Your solution also works, and is certainly simpler than my current method. I'll have to decide if I want the scrollbar to display the actual position or more tangible position. At least for me, it feels like the app is loading many objects, and therefore is slower, when the scrollbar is so small.

ACoolie