在顶部添加单元格时,使UITableView保持滚动位置

时间:2014-11-21 10:33:16

标签: ios objective-c uitableview core-data nsfetchedresultscontroller

好的,我需要的是当新元素添加到当前可见行之上时阻止tableview滚动。我正在使用带有大量对象的NSFetchedResultsController(如10 000),当我没有触及contentOffset时,reloadData非常流畅。

但是,当我尝试操作contentOffset以在插入新条目时保持滚动位置时,它开始冻结用户界面300毫秒,这对我不利。

有人建议在顶部添加新项目时如何将tableview保持在同一单元格的更好(更快)的解决方案吗?

这是我目前用于跟踪插入和移动contentOffset的代码。它不支持动画和不同的单元格大小。

- (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];
        }
    }
}

2 个答案:

答案 0 :(得分:2)

好的,经过一些让我指向正确方向的回应后,我想出了这个解决方案。虽然做了一些丑陋的坐标数学并且不支持不同的单元格大小,但它工作顺畅。希望它对某人有所帮助。

-(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];
    }
}

答案 1 :(得分:0)

您的任务可能熟悉许多新闻源应用程序列表视图。你看过那些应用了吗?他们可能会有某种解决方案。据我所知,保持表格视图位置的唯一方法是scrollRectToVisible:animated:

许多社交网络应用程序都有熟悉的方法来插入新的单元格。当有更新时,会在视图顶部显示小通知,并显示消息(" 5个新项目"),其中包含单元格并自动滚动到顶部。