iOS App Development iOS Swift Tutorial iOS8 Xcode App Design

iOS Swift Tutorial: UIPageViewController as user onboarding tool

UIPageViewController is another system provided view controller which can be used in a set of different usecases.

A page view controller lets the user navigate between pages of content, where each page is managed by its own view controller object. Navigation can be controlled programmatically by your app or directly by the user using gestures. When navigating from page to page, the page view controller uses the transition that you specify to animate the change.

The data source for a page view controller is responsible for providing the content view controllers on demand and must conform to the UIPageViewControllerDataSource protocol. The delegate object—an object that conforms to the UIPageViewControllerDelegate protocol—provides some appearance-related information and receives notifications about gesture-initiated transitions.

To understand the benefits of UIPageViewController lets go ahead and take a look at how mobile apps are onboarding users these days User Onboarding – Mobile Patterns or User Onboarding – Pttrns

Now that we know the use cases lets go ahead and implement them in our own app.

  • Create a single view application
  • On the main storyboard drag a PageViewController and a UIViewController as shown below

images

  • Then drag a UIImageView and place it on the UIViewController followed by a label on top of it.
  • At this point create a new file which is a subclass of UIViewController and give it a name PageContentViewController
  • Select the PageViewController and assign it a storyboard id PageViewController also select the attributes inspector and change the scroll direction to horizontal and the transition style to Scroll.
    images
    images
  • Similarly select the UIViewController you added and change the Storyboard id to PageContentViewController and also the class to PageContentViewControllerimages

At this point we have setup the layout for the app.

You might also like Building a photography inspiration app using Swift

Setting up PageContentViewController

In our page content view controller we had a label and an imageview.. Lets go ahead and create the IBOutlet. Open the Assistant Editor and CTRL+Drag from each of the UIelements to the PageContentViewController.swift file. Also PageContentViewController has the a few properties like the current Image, current Label, and what index the pageviewcontroller is showing. To maintain the state lets create those variables. At this point your page content view controller should look like this

images

//
//  PageContentViewController.swift
//  UIPageViewController
//
//  Created by Shrikar Archak on 1/15/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import UIKit

class PageContentViewController: UIViewController {

    @IBOutlet weak var heading: UILabel!
    @IBOutlet weak var bkImageView: UIImageView!

    var pageIndex: Int?
    var titleText : String!
    var imageName : String!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.bkImageView.image = UIImage(named: imageName)
        self.heading.text = self.titleText
        self.heading.alpha = 0.1
        UIView.animateWithDuration(1.0, animations: { () ->; Void in
            self.heading.alpha = 1.0
        })

    }
}

Setting up the UIPageViewController

Now lets go ahead and conform to UIPageViewControllerDataSource by implementing the necessary functions. When you created the project there was a default ViewController.swift and a ViewController on the storyboard we will be using those to setup our UIPageViewController.

Required functions

pageViewController:viewControllerAfterViewController:
pageViewController:viewControllerBeforeViewController:

AfterViewController and BeforeViewController are used for paging through a set of viewcontrollers.viewControllerAtIndex function just checks if the index is within valid bounds and return the viewcontroller at that index. Similarly the viewControllerAfterViewController and viewControllerBeforeViewController check for the index within bounds and paginate through the view controllers either by incrementing/decrementing the index. If the index is outside the bounds we return nil.

Make sure you set the pageviewcontroller delegate to self in this case.

//
//  ViewController.swift
//  UIPageViewController
//
//  Created by Shrikar Archak on 1/15/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {


    let pageTitles = ["Title 1", "Title 2", "Title 3", "Title 4"]
    var images = ["long3.png","long4.png","long1.png","long2.png"]
    var count = 0

    var pageViewController : UIPageViewController!

    @IBAction func swipeLeft(sender: AnyObject) {
        println("SWipe left")
    }
    @IBAction func swiped(sender: AnyObject) {

        self.pageViewController.view .removeFromSuperview()
        self.pageViewController.removeFromParentViewController()
        reset()
    }

    func reset() {
        /* Getting the page View controller */
        pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as UIPageViewController
        self.pageViewController.dataSource = self

        let pageContentViewController = self.viewControllerAtIndex(0)
        self.pageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)

         /* We are substracting 30 because we have a start again button whose height is 30*/
        self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height - 30)
        self.addChildViewController(pageViewController)
        self.view.addSubview(pageViewController.view)
        self.pageViewController.didMoveToParentViewController(self)
    }

    @IBAction func start(sender: AnyObject) {
        let pageContentViewController = self.viewControllerAtIndex(0)
        self.pageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        reset()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) ->; UIViewController? {

        var index = (viewController as PageContentViewController).pageIndex!
        index++
        if(index >= self.images.count){
            return nil
        }
        return self.viewControllerAtIndex(index)

    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) ->; UIViewController? {

        var index = (viewController as PageContentViewController).pageIndex!
        if(index <= 0){
            return nil
        }
        index--
        return self.viewControllerAtIndex(index)

    }

    func viewControllerAtIndex(index : Int) ->; UIViewController? {
        if((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
            return nil
        }
        let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageContentViewController") as PageContentViewController

        pageContentViewController.imageName = self.images[index]
        pageContentViewController.titleText = self.pageTitles[index]
        pageContentViewController.pageIndex = index
        return pageContentViewController
    }

    func presentationCountForPageViewController(pageViewController: UIPageViewController) ->; Int {
        return pageTitles.count
    }

    func presentationIndexForPageViewController(pageViewController: UIPageViewController) ->; Int {
        return 0
    }

}

About the author

Shrikar

Backend/Infrastructure Engineer by Day. iOS Developer for the rest of the time.

  • http://novaweb.pt/ Pedro Araújo

    Hey, thanks a lot for the tutorial. It was really helpful for me to get started.

    I was wondering if you could help with a part I think you didn’t cover in this tutorial which is the background image sizes.

    I’ve been playing around and just can’t get the sizes right for all iPhone models. How do you make the view behave correctly in every screen size?

    Thanks a lot for this,
    Pedro Araújo

  • Koji

    I want to know about that.
    where should I connect the IBActions swipeLeft() and swiped() ?

  • Isadora Sophia

    Hey there! I was just trying myself and compiling your code (thanks for the tutorial, btw!) but I keep getting the error: “‘NSUnknownKeyException’ – this class is not key value coding-compliant for the key CurrentImageView.” Apparently, it is taking my UIImageView as nil. Any guesses?
    Thanks!

    • http://shrikar.com shrikar

      Looks like the connections are not setup properly.. Would you mind checking the connections tabs in xcode and see if all the iboutlets are properly set.

      • Isadora Sophia

        Oh, that was it! Thanks for the help, now it’s working 😀

        • http://shrikar.com shrikar

          Cool!

  • Alex

    Hi!

    I want to ask about UIPageControl, how can I do page control at the center, I create own page control, but can’t to join it with UIPageViewController. I found some solution on obj-c “[self.pageControl setCurrentPage:index]”. How it will be on swift or maybe you have another solution?
    Thanks!

    • http://shrikar.com shrikar

      Sorry I am not able to understand the question. What do you mean by can’t to join it?

  • Daniel Chu

    Hey there,

    Why is presentationIndexForPageViewController always returning 0?

    • http://shrikar.com shrikar

      It should be index.

  • Shivam

    Hi,
    I am a newbie in iOS development. Can you tell me that how can I add tap gesture for UIPagviewController so that when user taps, it goes to the next view.

    • Desh

      Anyone knows how to do this?

      • http://shrikar.com shrikar

        I think it would be better to have a simple UIViewcontroller instead of UIPageViewController and attach a UITapgesture recognizer. On tap you could basically simulate the swiping action by using the slide UIAnimations.

  • Tiago Taveira

    Hi shrikar,

    Could you explain why you use a Page View Controller instead of a UIScrollView? I’d like to better understand the differences between the two!

  • Yestay Muratov

    I have problem with IBOUTlets . it is not initializing and giving me error nil. So when the code try to set image value to ImageValue.image, the imageView is nil ! Why is that so?

    • http://shrikar.com shrikar

      Go to the connection editor in xcode and see if the IBOutlets are hooked properly.

  • xCodeR

    Did you ever make this “next tutorial” or you just gonna sit on that one while we all wait…

    • Shrikar

      Hey sorry man I forgot to update.. I started with something but then figured out it was not relevant so please ignore. Sorry for the confusion.

    • http://shrikar.com shrikar

      Hey xCodeR sorry about that I initally tried adding some functionality but then it didn’t make sense to include it so ignore it for now..

      • Beth Knight

        Did you show how to hook them up?

  • Kakubei

    Your “greater than” symbols (>) are being translated to HTML equivalents in your code, thought you might like to know.

    • http://shrikar.com shrikar

      Thanks Kakubei. Fixed it..

      • Kakubei

        Thank YOU for a nice tutorial!

  • 윤형선

    Hi

    I Have question..

    I want get ‘current page index’ When paging is finished animation..
    ‘current page index’ is going to use it at the top of the label
    viewControllerAfterViewController function is not often called..

    • http://shrikar.com shrikar

      Are you saying that the on swipe its not going to the next view? IF so make sure the datasource are set properly

  • Grant Powell

    How do you “create a new file which is a subclass of UIViewController”? Thanks

  • Joel Wasserman

    UIView.animateWithDuration(1.0, animations: { () ->; Void in

    self.heading.alpha = 1.0

    })

    There’s a typo. There shouldn’t be the semi colon after -> ….. just an FYI

    • http://shrikar.com shrikar

      Thank Joel I will fix it.. That some crap with the wordpress html to code plugin. I wil fix it

  • Grant Powell

    I copied and pasted the code into XCode and show 17 errors immediately after pasting – cannot run.

    • Grant Powell

      Maybe it has something to do with the “Required Functions”? Where do I add those?

      • Clayton William Griffith

        Hey, still working on this too but looks like some of the symbols in the code was carried over wrong when he initially pasted it in here. As an example, you’ll see “if (index <=0)” in the code which should be “if (index <= 0)". You might start there.

  • Eddy

    Hello want to ask, how do we change the colour of the page control?

  • Göktuğ Yılmaz
  • Goncalo Girao

    Hey, thanks a lot for the tutorial! I inserted this code inside a running app. Two questions/glitches; The app now starts on this pageView instead usual workflow. I tried to condition in segue (if segue.Identifier…) but then it only shows black screen. And how can I insert a fixed background image? TY

  • hameed

    AfterViewController Says Expected Expression in list of expression

    if( index > = self.images.count)
    {
    return nil
    }

  • Bong Khàï

    I do not understand at all

  • Leang Socheat

    It’s good tutorial… but I have some wonder. on your video above is show us about how it work. but when swipe it to get new page, why it immediately change the position of title?.
    I follow that problem and I try to avoid with this problem(change position immediately after slide) , in iOS 9+ work well not change. but in iOS 8 I face on that problem. anyone have solution this pls help . thank you in advance

  • Nik Edwards

    how to make it auto scroll?

/* ]]> */