使用UIScrollView进行分页的无限滚动
看似简单的技术,它得益于良好的心理模型来解开纠结
《无限滚动》,乔什和伊丽莎,WWDC 2011
iOS的无限滚动技术已众所周知多年,尽管有时甚至在今天也被认为很困难。 我第一次听说这件事是在2011年Josh&Eliza的WWDC演讲中。
通常,当您在滚动视图上向左滑动时,您会得到屏幕向左流动的印象。 但是,iOS只是通过将视口移到相反的方向来给您那种印象。 在下一个示例中,在向左滑动显示黄色部分之前,仅滚动视图的柠檬绿色部分可见。 在此示例中,整个滚动视图没有更改,但是只有视图端口向右移动以显示黄色部分。
通过这种正常的滚动行为,您将陷于Content View的边缘。
Josh&Eliza引入的技术的关键思想是在UIScrollViewDelegate.scrollViewDidScroll(...)
执行以下操作
- 将内容视图移向滑动方向,并使视口始终位于内容视图的中心
- 在内容视图的新开放空间中添加新内容
使用这种技术,我们永远不会卡在Content View的边缘,因为View端口始终位于中心。 只要我们的程序将新内容正确地输入到开放空间中,我们就可以永远滚动。
UIScrollViewDelegate.scrollViewDidScroll(...)
是描述滚动调整动作的最佳位置之一,因为它在每次查看端口移动时以及在实际布局/渲染发生之前都会被调用。
翻页滚动
问题
上面的技术很容易实现,非常有效。 除了需要分页时。
UIScrollView
有一个名为isPagingEnabled
的标志。 通过仅将此标志设置为true
,滚动可以在每一页上完美地捕捉。 请查看以下“动物偷看”示例应用程序示例。 您希望每个滚动都像在gif中看到的那样捕捉每个动物。 否则,它不是peek-a-boo🙂
我敢打赌,如果滚动仅停在某一点,孩子们将不会高兴,因此这是将其作为无限滚动视图的动机。
如果我们在此应用程序中天真地实现Josh&Eliza的技术, 它将破坏 UIScrollView
的分页功能 。 请记住,Josh&Eliza的主要想法是将视口保持在Content View的中心。 这使iOS认为滚动视图根本不在页面上移动。 结果,页面捕捉将永远不会正确发生。 所以……休斯顿,我们有一个问题。
解
与这个世界上的一切一样,有一个解决方案。 这是一个相当容易的过程。 解决方案的关键思想是
我们不需要经常调整视口。 Josh&Eliza为简化代码做了太多事情。
每次 调用 scrollViewDidScroll(...)
时, Josh&Eliza 都会进行视口调整。 但是实际上,当您考虑它时,我们真正需要它只有两个时间 。
- 当视口移至右侧时, 下一页内容将占据视口的大部分。 我们需要将视口移回左侧,以使视口在不久的将来不会卡在右侧。
- 当视口移到左侧时, 上一页内容占据了视口的大部分。 我们需要将视口向前移至右侧,以使视口在不久的将来不会卡在左侧。
您只需要针对页面大小调整视口,然后填写下一个内容。 这项调整的妙处在于,它使普通滚动都停留在每页的边缘,因此页面捕捉也很愉快。
码
我正在展示我上面演示的“ Animal peek-a-boo”示例的示例代码。 在示例中,滚动视图的每一页都有一个UIImageView
和一个UIImage
。 如上面多次说明, 无限滚动视图只需要三个页面 ,因此我们准备了三个UIImageView
的数组UIImageView
用它们(称为imageViews
)。 每当我们调整页面的位置时,我们都会调整那些UIImageView
的框架,并将新图像设置为UIImageView
。
相应的代码如下:
摘要
当您想使用UIScrollView
实现无限滚动视图时,可以通过调整contentOffset
及其内容视图的框架来实现。
当您需要分页时,vanilla方法将不起作用,但是您可以通过限制调整时间来应用相同的概念。 我阅读的某些文章需要5页,但实际上,您只需要在页面中间进行调整就可以准备3页。
我认为您也可以使用UIPageViewController
做类似的事情,但是它将需要UIViewController
成为页面成员,并且在许多情况下,您不想为每个成员创建UIViewController
。 还需要注意的是, UICollectionView
和 UITableView
继承自 UIScrollView
并且在使用这些常见UI元素时,该技术应直接适用。
“ Animal peek-a-boo”示例代码在github中。 如果您有兴趣,请随时查看,如果有任何反馈等,请让我知道。