【问题标题】:UITableView insert rows without scrollingUITableView 插入行而不滚动
【发布时间】:2011-08-17 21:11:19
【问题描述】:

我有一个从网络服务中提取的数据列表。我刷新数据,我想在当前数据上方的表格视图中插入数据,但我想在表格视图中保留我当前的滚动位置。

现在我通过在当前部分上方插入一个部分来完成此操作,但它实际上是插入、向上滚动,然后我必须手动向下滚动。在此之前,我尝试在表格上禁用滚动,但这也不起作用。

这看起来很不稳定,而且看起来很老套。有什么更好的方法来做到这一点?

[tableView beginUpdates];

[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

NSUInteger iContentOffset = 200; //height of inserted rows

[tableView setContentOffset:CGPointMake(0, iContentOffset)];

【问题讨论】:

  • 我正在尝试模仿大多数 Twitter 应用程序所做的事情。他们在新旧数据之间放置了一个分隔符(表示有新数据),并允许您按时间顺序滚动时间轴。
  • 插入后有没有试过scrollToNearestSelectedRowAtScrollPosition:animated:scrollToRowAtIndexPath:atScrollPosition:animated:
  • 您能解释一下为什么scrollToRowAtIndexPath:atScrollPosition:animated:scrollToNearestRow... 不适合您吗?编辑:哈,尼克打败了我。
  • @Nick @Josh 我可以使用setContentOffset: 完成此滚动,但我真正的问题是如何避免滚动。此外,根据我的尝试,这两个选项的效果都比 setContentOffset: 差。
  • 只是为了理解这一点:例如,您在索引 13 处,并且喜欢在 3 处插入一行,这不应该滚动并将 pos 保持在 13,对吧?

标签: iphone objective-c uitableview


【解决方案1】:

如果我正确理解你的任务,

我是这样做的:

if(self.tableView.contentOffset.y > ONE_OR_X_ROWS_HEIGHT_YOUDECIDE
{

    self.delayOffset = self.tableView.contentOffset;
    self.delayOffset = CGPointMake(self.delayOffset.x, self.delayOffset.y+ insertedRowCount * ONE_ROW_HEIGHT);

    [self.tableView reloadData];

    [self.tableView setContentOffset:self.delayOffset animated:NO];    


}else
{
    [self.tableView insertRowsAtIndexPath:indexPathArray   WithRowAnimation:UITableViewRowAnimationTop];

}

使用此代码,如果用户位于表格的中间而不是顶部,则 uitableview 将重新加载新行而没有动画和滚动。 如果用户在表格的顶部,他将看到行插入动画。

请注意代码,我假设行的高度相等,如果不相等,只需计算您要插入的所有新行的高度。 希望对您有所帮助。

【讨论】:

    【解决方案2】:

    我发现获得所需行为的最佳方法是根本不为插入设置动画。动画造成了波动。

    我打电话给:

    [tableView reloadData];
    
    // set the content offset to the height of inserted rows 
    // (2 rows * 44 points = 88 in this example)
    [tableView setContentOffset:CGPointMake(0, 88)]; 
    

    这使得重新加载出现在内容偏移更改的同时。

    【讨论】:

    • 当表格视图未满时这仍然会造成问题......有什么想法吗?
    【解决方案3】:

    对于寻找 Swift 3+ 解决方案的更多观众:

    您需要保存UITableView 的当前偏移量,然后重新加载,然后将偏移量设置回UITableView

    func reloadTableView(_ tableView: UITableView) {
        let contentOffset = tableView.contentOffset
        tableView.reloadData()
        tableView.layoutIfNeeded()
        tableView.setContentOffset(contentOffset, animated: false)
    }
    

    调用者:reloadTableView(self.tableView)

    【讨论】:

    • 在顶部添加新行怎么办?
    【解决方案4】:

    只需在endUpdates 之前调用setContentOffset,这对我有用。

    [tableView setContentOffset:CGPointMake(0, iContentOffset)];
    [tableView endUpdates];
    

    【讨论】:

      【解决方案5】:

      我正在使用自行调整大小的单元格,估计的行高毫无用处,因为单元格的大小可能会有很大差异。所以计算 contentOffset 对我不起作用。

      我最终得到的解决方案非常简单,而且效果很好。 所以首先我应该提一下,我有一些辅助方法可以让我获取索引路径的数据元素,反之亦然 - 数据元素的索引路径。

      -(void) loadMoreElements:(UIRefreshControl *) refreshControl {
        NSIndexPath *topIndexPath = [NSIndexPath indexPathForRow:0 inSection:0]
        id topElement = [myModel elementAtIndexPath:topIndexPath];
      
        // Somewhere here you'll need to tell your model to get more data
        [self.tableView reloadData];
      
        NSIndexPath *indexPath = [myModel indexPathForElement:topElement];
      
        [self.tableView scrollToRowAtIndexPath:indexPath 
                              atScrollPosition:UITableViewScrollPositionTop 
                                      animated:NO];
      
        [refreshControl endRefreshing];
      }
      

      【讨论】:

        【解决方案6】:

        这里没有一个答案真的对我有用,所以我想出了我的解决方案。这个想法是,当您下拉刷新表格视图(或使用新数据异步加载它)时,新单元格应该静默出现并位于表格视图的顶部,而不会干扰用户当前的偏移量。所以这里有一个可行的解决方案(几乎,带有警告

        var indexpathsToReload: [IndexPath] = [] //this should contain the new indexPaths
        var height: CGFloat = 0.0
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
            UIView.performWithoutAnimation {
        
                self.tableview.reloadSections(NSIndexSet(index: 0) as IndexSet, with: .none) 
                self.tableview.layoutIfNeeded()
                indexpathsToReload.forEach({ (idx) in
                    height += self.feed.rectForRow(at: idx).height
                })
                let afterContentOffset = self.tableview.contentOffset                             
                let newContentOffset = CGPoint(x: afterContentOffset.x, y: afterContentOffset.y + height)
                self.tableview.setContentOffset(newContentOffset, animated: false)
            }
        }
        

        CAVEAT(警告) 如果您的 tableview 不是“完整的”,即它只有几个单元格,则此技术将不起作用。在这种情况下,您还需要增加表格视图的contentSize 以及contentOffset。一旦我弄清楚了,我会更新这个答案。

        解释: 基本上,我们需要将tableViewcontentOffset 设置为重新加载之前的位置。为此,我们只需计算使用预填充的indexPath 数组添加的所有新单元格的总高度(可以在获取新数据并将它们添加到数据源时准备好),这些是indexPaths新的细胞。然后我们使用rectForRow(at: indexPath) 使用所有这些新单元格的总高度,并将其设置为重新加载后tableViewcontentOffsety 位置。 DispatchQueue.main.asyncAfter 不是必需的,但我把它放在那里是因为我只需要给 tableview 一些时间来反弹到它的原始位置,因为我正在以“拉动刷新”的方式进行操作。另请注意,在我的情况下,afterContentOffset.y 的值始终为0,因此我可以在那里硬编码0

        【讨论】:

        • 很好的答案。 layoutIfNeeded 是这里的关键。我不会太在意“tableView 未满”的情况。如果内容没有填满整个屏幕,插入更多数据不会首先使 tableView 滚动。
        • 不幸的是,这不适用于自调整大小的单元格。在这种情况下,contentSizecontentOffset 都不是稳定的、可预测的值。 UICollectionViewController 让它看起来像一个表格视图,也许有人更幸运。
        • @DrMickeyLauer 是的,你是对的。该解决方案不适用于动态高度单元。我可以通过计算 heightForRow 来实现这一点,但由于它不能在后台线程中完成,所以在计算时有点滞后。您的 Collection view 评论是正确的,因为它比 TableView 更强大,您可能想看看 Instagram 的 IGListKit。刚开始完全理解 IGListKit 的工作原理需要一点时间,但它可以做很多事情。
        • 对 IGListKit 的一个警告是,重构现有代码以使用 IGListKit 可能需要相当多的工作,因为它仅使用 UICollectionView 并且实现与内置的 Collection 视图完全不同。跨度>
        猜你喜欢
        • 2016-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-16
        • 2019-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多