Make UITableView keep scroll position when adding cells at the top

Ok, so I need to prevent the table from scrolling when new items are added over the current visible row. I use NSFetchedResultsController with a lot of objects (e.g. 10,000), and reloadData works pretty smoothly when I don't touch contentOffset.

However, when I try to manipulate the contentOffset to maintain the scroll position when inserting new records, it starts to freeze the user interface in about 300 ms, which is bad for me.

Can anyone suggest a better (faster) solution on how to save the table in one cell when adding new items at the top?

And this is the code that I am currently using to track the insertion and change of contentOffset. It does not support animation and cell size.

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { switch(type) { case NSFetchedResultsChangeInsert: [_insertions addObject:@(newIndexPath.row)]; break; } } - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { _insertions = @[].mutableCopy; } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { NSMutableArray *copy = _insertions.mutableCopy; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSSortDescriptor *lowestToHighest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]; [copy sortUsingDescriptors:[NSArray arrayWithObject:lowestToHighest]]; dispatch_sync(dispatch_get_main_queue(), ^{ for (NSNumber *i in copy) { [self p_delayedAddRowAtIndex:[i integerValue]]; } [self.tableView reloadData]; }); }); } - (CGFloat)p_firstRowHeight { return [self tableView:[self tableView] heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; } -(void)p_delayedAddRowAtIndex:(NSInteger)row { NSIndexPath *firstVisibleIndexPath = [[self.tableView indexPathsForVisibleRows] firstObject]; if (firstVisibleIndexPath.row >= row) { CGPoint offset = [[self tableView] contentOffset]; offset.y += [self firstRowHeight]; if ([self.tableView visibleCells].count > self.tableView.frame.size.height / [self firstRowHeight]) { [[self tableView] setContentOffset:offset]; } } } 
+7
ios objective-c uitableview core-data nsfetchedresultscontroller
source share
2 answers

Well, after some answers that pointed me in the right direction, I came up with this solution. It works smoothly, although it does some ugly mathematical coordinates and does not support different cell sizes. Hope this will be helpful to someone.

 -(void)controllerWillChangeContent:(NSFetchedResultsController *)controller { NSIndexPath *firstVisible = [[self.tableView indexPathsForVisibleRows] firstObject]; self.topVisibleMessage = [self.fetchedResultsController objectAtIndexPath:firstVisible]; NSIndexPath *topIndexPath = [self.fetchedResultsController indexPathForObject:self.topVisibleMessage]; self.cellOffset = self.tableView.contentOffset.y - topIndexPath.row * [self firstRowHeight]; } -(void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView reloadData]; if (self.topVisibleMessage) { NSIndexPath *topIndexPath = [self.fetchedResultsController indexPathForObject:self.topVisibleMessage]; CGPoint point = {0, topIndexPath.row * [self firstRowHeight] + self.cellOffset}; [self.tableView setContentOffset:point]; } } 
+2
source share

Your task may be familiar with the large number of newspaper application list views. You watched these applications. They may have some solutions. As I know, the only way to save a position as a table is scrollRectToVisible:animated:

Many social media apps have a familiar approach to inserting new cells on top. When there are updates, a small notification appears at the top of the window with a message ("5 new elements"), which inserts cells along the edges and automatically scrolls up.

0
source share

All Articles