To Infinity. Literally.
There came a time, not so long ago now, where a user experience-related issue required me to think about things differently. I borrowed a colleague's phone — an Android —to have a look for some platform-agnostic ideas, and the solution jumped out at me within moments — an infinitely paging UIScrollView.
My first thought was one of regret. Why, why did I need to make my own life so very difficult? Any iOS developer will likely understand the struggles that Apple’s sometimes nonsensical limitations present to the individual. It’s borderline torture — first you’ve got to think differently, then you’ve got to get whatever it is working well enough to ship.
My second thought was to search Github, Google, and the omnipresent StackOverflow for this idea. My five minutes of searching revealed to me that many had tried to solve this problem and indeed done so, but that most of the solutions had unresolved issues or were written almost five years ago. Because of this, I decided to write my own in Swift. 🎉
Doing The Thing!
We’ll be making something like this:
The first thing you’re going to do, is create a new project in Xcode which uses the Single View Application template. Once this is done, create a new Cocoa Touch Class file, call it something like LoopyScrollThingy and ensure that its subclass is set to UIScrollView.
If any of the above felt like suddenly being asked to translate ancient Chinese battle hymns into Shakespearean English, please turn back now. It only gets worse from here.
Open up your new file if Xcode forgot how or you haven’t already, select everything, then paste this in:
- 1: Our variables.
- 2: We need this type of initialiser because we’ll be using Interface Builder to keep things simple a bit later on.
- 3: The setup() function adds the views you connected to the scroll view for you. It also sets its own contentSize.
- 4: The loadScrollViewWithPage() function, which takes a page parameter, adds the required page/s to the scroll view.
- 5: Method implemented from UIScrollViewDelegate. The scrollViewDidScroll method is responsible for updating the scroll view as you move.
- 6: Method implemented from UIScrollViewDelegate. The scrollViewDidEndDecelerating method gets called when the scroll view stops moving, and then updates the content offset if you’re at the end of the pageable content. This is the key.
Did I mention I’m really glad Swift 3 is removing unnecessary repetition of things like color and scrollView? 'Cause I am!
Making The Thing Work! Kinda!
Go ahead and open up your ViewController.swift file, or whichever UIViewController you want this contained in. This will likely be a menu or your very own onboarding process.
Declare yourself a weak IBOutlet, named whatever you like, ensuring that its of type LoopyScrollThingy?, then save.
Now, open up your storyboard and find the associated UIViewController container. Drag a UIScrollView object onto it, then give it AutoLayout constraints as follows:
Wire up the outlet you created before to this scroll view object, then save. Make sure that the object has its class set to LoopyScrollThingy too.
Back in your View Controller, and find the viewDidLoad() method. Highlight the entire method, and paste the following in:
- 1: We’re not using Interface Builder yet, but we will soon! 🙏 We’re creating three UIView objects, each with different colours, so we can tell them apart during scrolling.
- 2: We tell the scroll view how many pages we want, then give it the array of views.
- 3: Then we call setup() which sets up the different thing! 🎉
If you did the different thing properly, running the app in the Simulator will show you how well you did! You’ll see your first page displayed, and a quick swipe to the left gives you the 2nd one, etcetera.
Note: This can still break if you go too quickly at this stage. It’s designed to be paged at an "ordinary" pace.
Putting A UIPageControl On The Thing!
You’ll notice that there’re no page dots to speak of. I'm of an unrelenting, slightly manic belief, that this control is absolutely necessary for the user to know they can scroll the content. So we’ll add that now, in a few simple steps.
- Add a private UIPageControl variable to the LoopyScrollThingy class.
- Then replace the setup() method with this:
- And the scrollViewDidScroll() method with this:
Run the app and see that, YAAAAAS! It shows the page dots! 🎉
Building An Interface For The Thing!
If you don’t already feel like you can do everything ever, open up your View Controller again, and delete the three view objects and the line which feeds them to the scroll view. Your new viewDidLoad() should look similar to this:
Then open up the LoopyScrollThingy class, and change viewObjects into an IBOutlet, and add some extra UIView objects to your UIViewController’s container in the storyboard, like so:
You can put anything in them, really. Images, if you want, or cuss words. Whatever floats your boat. However as mentioned above, this effect works better with 3+ screens however we’ll rectify that in Part 2 of this tutorial series.
Now wire up the scroll view’s viewObjects property to each one of the views. Pay careful attention to the order, because this is the order in which they will appear on screen.
Run the app again. This time, the views you connected in Interface Builder are now the panels of your scroll view! You’ll see it doesn’t work perfectly, but that’s what we’ll fix in our next attempt at thinking differently.