在uitableview上加载更多项目向上滚动

时间:2014-12-11 18:03:00

标签: ios uitableview

我正在发表评论UITableView。我在底部有最新评论,而在顶部有较旧的评论。

我将评论保存在NSMutableArray中。当我到达顶部时,我想加载更多较旧的评论。我怎么能这样做?

2 个答案:

答案 0 :(得分:3)

您需要一些方法:

获取方法 - 这取决于您如何实现数据检索。默认情况下,您只显示最近的10条评论。如果您的数组只包含这10个注释,那么您需要一种机制来执行本地或远程数据库的提取,以获取另外10个附加注释并将它们添加到数组中。如果数组总是包含与该帖子相关的所有注释(在这种情况下为56条评论),则不需要额外的提取,但有人可能认为检索和存储用户可能永远看不到的注释效率低下。为了显示正确数量的评论,需要进行一些数学运算,但我会稍微讨论一下。

更新表格 - 您需要在查看顶部单元格时调用[tableView reloadData],因为将显示更多数据。根据数组是仅包含要显示的注释还是与帖子相关的所有注释,您可能还需要调用上面的fetch方法。例如,如果您的数组只包含10条注释,并且您需要提取额外的10条注释,那么您首先需要执行提取,并且当提取完成时,您需要调用reloadData。如果数组包含所有56条评论,那么您需要更新numCommentsToShow属性,如下所述,然后立即致电reloadData

更新触发器 - 当查看顶部单元时,需要调用重新加载表的方法;但是,当视图首次出现时,您不希望立即调用重新加载表。有很多种方法可以实现这一点,这实际上取决于当顶部单元格部分可见,完全可见,甚至即将被查看时(实际上对于用户来说可能是最干净的),是否要显示其他注释。一个简单的实现(但不一定是最理想的)是包含调用以cellForIndexPath方法更新表。如果indexPath.row为0,则调用update。您将首先测试,但在允许更新之前,视图不是刚刚加载。您还希望返回的单元格包含在更新之前出现的注释,因为在获取之后将再次调用cellForIndexPath方法。如果您希望在允许更新之前单元格完全可见,您可以执行以下操作:Best way to check if UITableViewCell is completely visible

数学 - 如果数组只包含要显示的注释,我实际上会将NSMutableArray设为NSArray。当你得到fetch的结果时,我只是将这20个注释保存在一个全新的数组中,并使注释数组属性只引用新数组。然后拨打reloadData。否则,在用户滚动时将注释添加到NSMutableArray对我来说听起来有点危险,但我可能错了。顺便说一句,有些人实际上可能只对10个下一个评论(即11-20)执行获取,但如果允许用户编辑他们的评论,我会检索1-20,因为最近的评论是最有可能的评论。更改。在获取之后,数组将包含20条注释,它们应该是最少到最近的顺序。这使得cellForIndexPath方法变得更加容易,indexPath.row方法应该只检索每个单元格的索引numberOfRowsInSection处的注释。当前正在查看的单元格(更新前的单元格)将在索引处等于刚刚拉出的新项目的数量。

如果数组包含与帖子相关的所有注释(在我的示例中为56),那么您需要拥有一个属性,其中包含要在此给定时间点显示的注释数。首次出现视图时,此属性设置为某个默认编号(在我的示例中为10)。 numCommentsToShow方法需要返回此属性,我将调用cellForIndexPath。但是,numCommentsToShow需要检索数组中的正确项。如果它们从最近到最近排序,您实际上需要在索引处检索注释,该注释等于数组中的注释数量indexPath.rownumCommentsToShow。这样做有点令人困惑。当您调用更新时,需要将setContentOffset增加一些增量(在我的示例中)10,但是您应该小心不要将其增加到大于数组中的项目数。因此,您实际上应该通过增量的最小值和尚未显示的注释量来增加它。

滚动问题 滚动时可能会遇到一些问题,因为表格顶部的单元格实际上会成为表格的中间单元格之一。因此,该表可能会在更新后跳转到顶部。您可以使用一些数学运算来确保更新后表格的滚动位置已设置,以便用户看到滚动的位置没有改变,实际上它已经改变了。如果用户在表尚未更新时向下移动表,则会发生更加困难的挑战。

在解释如何做到这一点之前,这些天的趋势似乎是避免这种情况。如果你看看Facebook应用程序,从来没有真正无限滚动到顶部。有一个按钮可以加载自动滚动到顶部的较新帖子。对帖子的评论从最近的评论开始在顶部开始,您可以点击按钮以查看更多评论"看到更近期的。并不是说这是一个良好的用户体验,但编程修复可能会比它的价值更加混乱。

但无论如何,为了解决滚动问题,我会使用UITableView方法,因为UIScrollView继承自scrollToRowAtIndexPath。您不应该使用UITableView的{​​{1}}方法,因为当前滚动位置可能不会与任何给定行完全对齐。您应确保animated参数设置为NO。您想要滚动到的确切位置涉及一些数学和时间。在获取数据时,用户可能已滚动到新位置,因此您已偏移当前位置,而不是在获取之前的位置。当提取完成并重新加载表时,您需要立即调用setContentOffset。新的y位置需要是当前滚动y位置+到显示行高度的新注释数量。

但是有一点问题。当提取完成时,视图可能处于滚动的中间。此时调用setContentOffset可能会导致滚动跳转或停顿。有几种方法可以处理这种情况。如果用户滚动离开表的顶部,则可能他/她可能不想再查看那些记录。因此,您可能会忽略所提取的数据或将其保存在其他位置而不将结果包含在表中。另一种方法是在使用新数据之前等待表格完成滚动。使用与重新加载表之前相同的速度和减速度继续滚动的第三种方法,但我不确定如何随意完成此操作。

对我而言,这比它更值得痛苦。

希望这有帮助,

答案 1 :(得分:0)

这是我在Xamarin中的解决方案,带有处理滚动问题的代码。我的标题是UIActivityIndi​​cator,当没有更多要加载的内容时,它会被删除。 DidScroll是在UITableViewSource的Scrolled方法中触发的事件。 DidScrollEventArgs包含从Scrolled方法传递的UISCrollView。

所以在UITableViewSource

    public class DidScrollEventArgs : EventArgs
    {
        public UIScrollView scrollView;

        public DidScrollEventArgs (UIScrollView scrollView) : base ()
        { 
            this.scrollView = scrollView;
        }
    }
    public event EventHandler<DidScrollEventArgs> DidScroll;
    public override void Scrolled (UIScrollView scrollView)
    {
        if (DidScroll != null)
            DidScroll.Invoke (this, new DidScrollEventArgs (scrollView));
    }

在我的UIViewController中

        source.DidScroll += async (object sender, MessageSource.DidScrollEventArgs e) => {

            nfloat height = e.scrollView.ContentSize.Height;
            nfloat contentYoffset = e.scrollView.ContentOffset.Y;

            if (contentYoffset == 0 && source.model.Count > 0 && height > MyBounds.Height && !isLoading && isMore) {

                isLoading = true;

                DateTime cutOff = source.model[0].SentDate.Value;

                //fetch more content
                List<MessageModel> newModels = await MessageAccess.GetMessagesAsync(ThreadId, cutOff, true);
                if (newModels.Count == 0){
                    isMore = false;
                    tableView.TableHeaderView = null;

                    isLoading = false;
                    return;
                }

                //add it on to the front of the current content
                models = newModels.Concat(source.model).ToList();
                source.model = models;

                tableView.ReloadData();

                //calculate where to scroll to (new height - old height)
                nfloat yOffset = tableView.ContentSize.Height - height;

                //scroll there
                tableView.SetContentOffset(new CGPoint(0, yOffset), false);

                isLoading = false;

            }
        };