在 uitableview 上加载更多向上滚动的项目
Posted
技术标签:
【中文标题】在 uitableview 上加载更多向上滚动的项目【英文标题】:Load more items scrolling up on uitableview 【发布时间】:2014-12-11 18:03:00 【问题描述】:我正在用 cmets 制作 UITableView
。我在底部有最新的 cmets,在顶部有较旧的 cmets。
我将 cmets 保存在 NSMutableArray
中。当我到达顶部时,我想加载更多较旧的 cmets。我该怎么做?
【问题讨论】:
【参考方案1】:你需要几个方法:
获取方法 - 这取决于您如何实现数据检索。假设默认情况下您只显示最近的 10 个 cmets。如果您的阵列仅包含这 10 个 cmets,那么您需要一种机制来执行本地或远程数据库的提取,以提取另外 10 个额外的 cmets 并将它们添加到阵列中。如果该数组始终包含与该帖子相关的所有 cmets(在本例中为 56 个 cmets),则不需要额外的 fetch,尽管有人可能会争辩说检索和存储用户可能永远不会看到的 cmets 效率低下。为了显示正确数量的 cmets,需要进行一些数学运算,但我稍后会介绍。
更新表格 -
在查看顶部单元格时,您将需要调用[tableView reloadData]
,因为将显示更多数据。根据数组是仅包含要显示的 cmets 还是包含与帖子相关的所有 cmets,您可能还需要调用上面的 fetch 方法。例如,如果您的数组仅包含 10 个 cmets,并且您需要提取额外的 10 个,那么您首先需要执行 fetch,当 fetch 完成时您需要调用reloadData
。如果数组包含所有 56 个 cmets,那么您将需要更新 numCommentsToShow
属性,如下所述,然后立即调用 reloadData
更新触发器 -
查看顶部单元格时需要调用重新加载表格的方法;但是,您不想在视图首次出现时立即调用重新加载表。有很多方法可以实现这一点,这实际上取决于您是否希望在顶部单元格部分可见、完全可见甚至即将被查看时出现额外的 cmets(这实际上对用户来说可能是最干净的)。一种简单的实现(但不一定是最理想的)是在cellForIndexPath
方法中包含更新表的调用。如果indexPath.row
为 0,则调用更新。尽管在允许更新之前不只是加载视图,但您需要先测试一下。您还希望返回的单元格包含更新之前出现的注释,因为在获取之后将再次调用 cellForIndexPath
方法。如果您希望单元格在允许更新之前完全可见,您可以执行以下操作:Best way to check if UITableViewCell is completely visible
数学 -
如果数组只包含要显示的 cmets,我实际上会将 NSMutableArray
设为 NSArray
。当您获得获取的结果时,我会将这 20 个 cmets 保存在一个全新的数组中,并让注释数组属性仅引用新数组。然后拨打reloadData
。否则,在用户滚动时将 cmets 添加到 NSMutableArray
对我来说听起来有点危险,但我可能是错的。顺便说一句,有些人实际上可能只在接下来的 10 个 cmets(即 11-20)上执行提取,但如果您允许用户编辑他们的 cmets,我会检索 1-20,因为最近的 cmets 最有可能改变。提取后,数组将包含 20 个 cmets,它们应该按从最少到最近的顺序排列。这使得cellForIndexPath
方法变得容易,它应该只检索每个单元格的索引indexPath.row
处的注释。当前正在查看的单元格(更新前的同一单元格)将位于等于刚刚拉出的新项目数的索引处。
如果数组包含与帖子相关的所有 cmets(在我的示例中为 56),那么您需要一个包含要在此给定时间点显示的 cmets 数量的属性。当视图首次出现时,此属性设置为某个默认数字(在我的示例中为 10)。 numberOfRowsInSection
方法需要返回这个属性,我将其称为numCommentsToShow
。但是,cellForIndexPath
需要检索数组中的正确项。如果它们从最近到最近排序,您实际上需要在索引处检索评论,该索引等于数组中 cmets 的数量减去numCommentsToShow
加上indexPath.row
。这样做有点令人困惑。当您调用更新时,您需要将numCommentsToShow
增加一些增量(在我的示例中)10,但您应该小心不要将其增加到大于数组中的项目数。因此,您实际上应该将其增加增量数量和尚未显示的 cmets 数量中的最小值。
滚动问题 您可能会在滚动时遇到一些问题,因为表格顶部的单元格实际上会成为表格的中间单元格之一。因此,表格可能会在更新后跳到顶部。您可以做一些数学运算来确保设置更新后表格的滚动位置,以便在用户看来滚动位置没有改变,而实际上它已经改变。如果用户在表格尚未更新的情况下将表格向下移动,则会出现更困难的挑战。
在解释如何做到这一点之前,这些天的趋势似乎是避免这种情况。如果您查看 facebook 应用程序,则永远不会真正无限滚动到顶部。有一个按钮可以加载自动滚动到顶部的较新帖子。帖子上的 cmets 以最近最少的评论开头,您可以单击按钮“查看更多 cmets”以查看最近的评论。并不是说这是良好的用户体验,但是编写修复程序可能会花费更多时间而不是值得。
但无论如何,为了解决滚动问题,我会使用setContentOffset
方法,因为UITableView
继承自UIScrollView
。您不应使用UITableView
的scrollToRowAtIndexPath
方法,因为当前滚动位置可能未与任何给定行完全对齐。您应该确保将animated
参数设置为NO
。您想要滚动到的确切位置涉及一些数学和时间。用户可能在获取数据时滚动到了新位置,因此您偏移了当前位置,而不是获取之前的任何位置。当提取完成并重新加载表时,您需要立即调用setContentOffset
。新的 y 位置需要是当前滚动 y 位置 + 到新 cmets 的数量才能显示 * 行高。
虽然有点问题。当获取完成时,视图可能会处于滚动的中间。此时调用setContentOffset
可能会导致滚动跳转或停止。有几种方法可以处理这种情况。如果用户已经从表格顶部滚动,那么他/她可能不想再查看这些记录。因此,您可能会忽略获取的数据或将其保存在另一个地方,而不会将结果包括在表中。另一种是等到表格完成滚动后再使用新数据。第三种方法以与重新加载表格之前相同的速度和减速度继续滚动,但我不确定您将如何临时完成此操作。
对我来说,这比它的价值更痛苦。
希望这会有所帮助,
【讨论】:
谢谢。 “滚动问题”是我目前遇到更多困难的部分。【参考方案2】:这是我在 Xamarin 中的解决方案,其中包含处理滚动问题的代码。我的标头是UIActivityIndicator
,当没有更多内容要加载时将其删除。 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;
;
【讨论】:
以上是关于在 uitableview 上加载更多向上滚动的项目的主要内容,如果未能解决你的问题,请参考以下文章
UITableView 在 reloadData 上向上滚动
每次我重新加载 tableview 部分时 UITableView 都不会向上滚动