Seemingly easy technique, which benefits from good mental model to untangle the kinks
Infinite Scroll by Josh & Eliza, WWDC 2011
The technique of infinite scroll for iOS is known for years, although it has been considered difficult at times even today. First time I heard about it was in WWDC talk by Josh & Eliza in 2011.
Normally, when you left swipe on a scroll view, you get the impression that your screen flows toward the left. However, iOS is just giving you that impression by moving the view port to the opposite direction. In the next example, only lime green part of the scroll view was visible before the left swipe showed the yellow part. In this example, entire scroll view didn’t change, but only the view port moved to the right to show the yellow part.
With this normal scrolling behavior, you will get stuck at the edge of Content View.
The key idea of the technique introduced by Josh & Eliza was to do the followings in
- Move the Content View to the direction of the swipe and keep viewport always in the center of the Content View
- Add new contents in the newly open space of the Content View
With this technique, we never get stuck at the edge of the Content View, because the the view port is always at the center. As long as our program keeps feeding new contents into the open space properly, we will be able to scroll forever.
UIScrollViewDelegate.scrollViewDidScroll(...) is one of the best places to describe adjusting actions of the scroll, because it is called every time the view port moves, and also before the actual layout/rendering happens.
Scroll with paging
The technique above is very easy to implement and it is very effective. Except when you need paging.
UIScrollView has a flag called
isPagingEnabled. By just setting this flag to
true, the scroll snaps beautifully at each page. Please look at the following example of “Animal peek-a-boo” sample app. You want every scroll to snap at each animal as you see in the gif. Otherwise, it is not peek-a-boo :)
I bet kids won’t be happy if the scroll just stopped at one point, so that’s the motivation to make this as an infinite scroll view.
If we naively implement Josh & Eliza’s technique in this app, it will break the paging functionality of the
UIScrollView. Remember that the key idea of Josh & Eliza was to keep the view port at the center of the Content View. This makes iOS think that the scroll view is not moving across the page at all. As a result, the page snap will never happen correctly. So… Houston, we have a problem.
As with everything in this world, there is a solution. And this is a fairly easy one. The key idea of the solution is that
We don’t need to adjust the view port that often. Josh & Eliza is doing too much for simplicity of the code.
Josh & Eliza did the adjustment of the viewport every time when
scrollViewDidScroll(...) was called. But actually, when you think about it, there are only two timings when we really need it.
- When the view port moved to the right, and the next page content occupied the majority of the viewport. We need to bring back the view port to the left so that view port won’t get stuck at the right edge in the near future.
- When the view port moved to the left, and the previous page content occupied the majority of the viewport. We need to bring the view port forward to the right so that view port won’t get stuck at the left edge in the near future.
You just need to adjust the viewport for the size of a page, and fill in the next content. Beautiful thing about this tweak is that it leaves the normal scrolling at the edge of each page, so the page snap works happily too.
I am showing the sample code from the “Animal peek-a-boo” sample I demonstrated above. In the sample, we have one
UIImageView and one
UIImage for each page of the scroll view. As illustrated above many times, we only need three pages for the infinite scroll view, so we prepare an array of three
UIImageViews and reuse them (called
imageViews). Whenever we adjust the position of the pages, we adjust the frame of those
UIImageView s, and set new images to the
The corresponding code looks as follows :
When you want to implement an infinite scrollview using
UIScrollView, you can do that by adjusting
contentOffset and frames of its content views.
When you need paging, the vanilla method will not work, but you can apply the same concept by limiting the timing of the adjustment. Some articles I read requires 5 pages, but in fact, you only need to prepare 3 pages by making adjustment in the middle of the page.
I think you can do similar thing with
UIPageViewController as well, but it will require
UIViewController to be the page member, and there are many cases where you don’t want to create
UIViewController for each member. It is also big to note that
UITableView inherits from
UIScrollView and the technique should be directly applicable when using those common UI elements.
The “Animal peek-a-boo” sample code is in the github. Please feel free to take a look if you are interested, and let me know if you have any feedback etc.