User On-boarding: Basic Swipeable Cards — iOS (Swift)— #DoItYourself

Abhinav Prakash
eureca
Published in
6 min readDec 24, 2017

User Onboarding, which requires user to do a specific set of tasks before application actually starts, is one of the most essential part of application . It actually has a potential to make or break the deal for a customer. This became all the more essential with the advent of ARKit, on which actually my team is working. Though once ARKit powered application are used by a user, the user gets well-versed with the twists, turns, jerks and moves of the application, the ask is not to help the user out during the first application launch. There have been some good posts on user onboarding especially for AR powered application such as by wayfair-design and inborn-experience.

When we took a dig into creating ourselves on-boarding our priority was to define a set of good practices, such as clean clear message and clear images, which would be used in a set of on-boarding cards, so to speak. We did find a lot of good repositories on github as well, of which Ramotion’s was fun. However, there was only so much we could do with the customisation when using these repositories, and then obviously there are issues you wait patiently to be resolved by the guys who created these frameworks in the first place.

I have always been a big fan of DIY (Do it yourself)— which understandably has helped me immensely since neither do I have a formal education in ComSci, nor I have been coding (at least seriously) for more than past 6 months. Hence, I took a stab at creating a custom on-boarding wireframe/ boilerplate in XCode from scratch.

Constraints:

  1. Most on-boarding frameworks I have seen, cover the device’s screen, which actually may be a decent idea, but not for Augmented Reality experiences, I think. The main View (which actually is the camera view for AR powered applications) should be lurking from all corners so that first-time users get just a little taste of what’s behind the on-boarding. Additionally, switching screen from on-boarding screens to main screen this way for a user is not jarring. After all, how many applications have views from camera as their mainstay!
    Hence, I really loved the one created by Liwei as below:

2. DIY

3. In the wireframe I should be able to fully customise where I want images and where I want the text.

4. I should be able to share it easily and people should be able to use and more importantly customise.

5. We need to have the way to change the size, location and orientation of the navigation dots along with introducing buttons, or else, next to the navigation dots.

Hence, after a lot of thoughts into the minutiae of UIPageViewController, UIPageControl, ContainerView, and more; few shots of Espresso; and a ‘huugggeee’ question on StackOverflow, we were finally able to get what we needed as below:

Onboarding Stack — Portrait
iPhone SE, Onboarding Stack — Landscape
iPad Air, Onboarding, Lansdcape
Storyboard

ViewController is the initial ViewController, within which we introduced a ContainerView. As ContainerView comes with its own default controller, we deleted that controller and embedded the OnboardingViewController (which we declared as a subclass of UIPageViewController) using Embed Segue.

Ultimately, we want to leverage the full power of UIPageViewController, don’t we! The three view controllers (views coloured in Orange, Green and Blue) will appear sliding within the OnboardingViewController.

We do need to have unique Storyboard IDs for these three, plus we would also need to make Restoration ID as Storyboard ID, since we would be using Restoration IDs, later for references.

We (dragged from Object Library) introduced UIPageControland some buttons in our main View, NOT within the ContainerView, please note. For that matter, you can introduce anything, but we felt ‘Next’ and ‘Skip’ button suited our needs.

Note: I might be using UIPageViewController and OnboardingViewController interchangeably. Please note that the latter is just a subclass of the former.

As you might observe we have full fledged views to add UIImage, Label, TextView, or, for that matter, Videos too — which gives anyone the ability to fully customise it, without depending on the frameworks/ packages. This made it a perfect DIY for us.

Code

We have the ViewController, within which we had dragged in UIPageControl. Hence, in order to update the UIPageControl, i.e., in order to update the progression of page swipe, we need to send message from the UIPageViewController to ViewController’s UIPageControl.

// ViewController.swift@IBOutlet weak var pageControl: UIPageControl!// Prepare the segue(Embed Segue) to UIPageViewController 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let onboardingViewController = segue.destination as
OnboardingViewController {
onboardingViewController.onboardingDelegate = self
}
}
// Update the count from UIPageViewController
func onboardingPageViewController(onboardingPageViewController: OnboardingViewController, didUpdatePageCount count: Int) {
pageControl.numberOfPages = count
}
// Update the index from UIPageViewController
func onboardingPageViewController(onboardingPageViewController: OnboardingViewController, didUpdatePageIndex index: Int) {
pageControl.currentPage = index
}

In order to get the make the updates, we need to send message from UIPageViewController to ViewController, for which delegation would serve well. We define our protocol in OnboardingViewController and make ViewController adopt our protocol.

// OnboardingViewController.swiftprotocol OnboardingViewControllerDelegate: class {
func onboardingPageViewController(onboardingPageViewController:
OnboardingViewController, didUpdatePageCount count: Int)
func onboardingPageViewController(onboardingPageViewController:
OnboardingViewController, didUpdatePageIndex index: Int)
}
// ViewController.swift
class ViewController: UIViewController, OnboardingViewControllerDelegate

That’s pretty much the heavy lifting.

Since UIPageViewController comes with its generic UIPageControl, which is very painful or rather impossible to customise beyond a certain boundary, we should not be using that generic UIPageControl. For example, some of the things you cannot do with the generic UIPageControl is changing the size, location and orientation of the navigation dots; introducing ‘Skip’ or any other button next to the navigation dots, etc. That’s why we started off with our own UIPageControl in the first place.

In the OnboardingViewController.swift, adopt UIPageViewControllerDataSource and UIPageViewControllerDelegate protocols. Declare delegate and datasource as self under ViewDidLoad() and instantiate your ViewControllers using Storyboard IDs.

class OnboardingViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {// InstantiatePV.. are functions which simply return the
// ViewController to be instantiated, please see full code for more.
private lazy var orderedViewController: [UIViewController] = {
return [instantiatePV1(),
instantiatePV2(),
instantiatePV3()]
}()
func instantiatePV1() -> UIViewController {
return (storyboard?.instantiateViewController(withIdentifier:
"1stPV"))!
}
...
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
onboardingDelegate?.onboardingPageViewController(
onboardingPageViewController: self,
didUpdatePageCount: orderedViewController.count
)
// For instantiating first ViewController (Orange) - Refer to full code for detail.if let firstViewController = orderedViewController.first {
setViewControllers([firstViewController],
direction: .forward, animated: true, completion: nil)
}
}// Use the following methods provided within any
// UIPageViewController to send index from OnboardingViewController
// to ViewController and we are done.
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if viewController.restorationIdentifier == "1stPV" {
return instantiatePV2()
} else if viewController.restorationIdentifier == "2ndPV" {
onboardingDelegate?.onboardingPageViewController(
onboardingPageViewController: self,
didUpdatePageIndex: 1
)
return instantiatePV3()
} else {
onboardingDelegate?.onboardingPageViewController(
onboardingPageViewController: self,
didUpdatePageIndex: 2)
return nil
}
}
...

That’s effectively all. We do need to write a few lines of code in our applicationDidLaunch() inside AppDelegate so that the user on-boarding screen is only visible for the first time user launches the Augmented Rality application, but I assume these 4 lines of code you would figure out anyway.

Please feel free to refer to github repository to check out/ use the codebase to whatever use you want.

Peace!

--

--