【问题标题】:Make UITableView keep scroll position when adding cells at top在顶部添加单元格时使 UITableView 保持滚动位置
【发布时间】:2015-01-19 11:21:46
【问题描述】:

好的,所以我需要在当前可见行上方添加新元素时防止 tableview 滚动。我将 NSFetchedResultsController 与很多对象(如 10 000)一起使用,当我不接触 contentOffset 时,reloadData 工作得非常顺利。

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

当新项目添加到顶部时,任何人都可以提出更好(更快)的解决方案吗?

这是我目前用来跟踪插入和移动 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];
        }
    }
}

【问题讨论】:

  • 这项工作是否可行:快照firstVisibleIndexPath in controllerWillChangeContent。在didChangeObject 中,记录在firstVisibleIndexPath 之前插入的行数。然后在controllerDidChangeContent 中,计算新行(通过添加计数),重新加载tableView,然后scrollToRowAtIndexPath 到新的indexPath。
  • 确保未设置估计的单元格大小

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


【解决方案1】:

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

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

【讨论】:

  • 我一直在考虑视觉通知和点击添加单元格,但由于我使用的是 NSFetchedResultsController,我无法直接访问数据源。有什么想法可以做到吗?
  • @kandreych 也许不要使用 NSFetchedResultsController。
  • frc 在我的应用程序中确实做了巨大的后台缓存和同步工作,因为它基本上是 rss 阅读器,所以我真的很想保留它
  • 我遇到了麻烦。我正在尝试使用 scrollRectToVisible 并且它仍然显示闪光/滚动,然后回到我想要的位置。实现这一点的最佳方法是什么?
【解决方案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];
    }
}

【讨论】:

    猜你喜欢
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 2019-01-21
    • 2015-07-18
    • 1970-01-01
    • 2020-09-22
    • 2011-04-08
    相关资源
    最近更新 更多