Posted by



This article documents Part 1 of the multi-part series that implements exhaustive and in-depth concepts about PageViewControllers in iOS.

Repository Specification

Download the project from GitHub  and follow along with the tutorial!

Details about UIPageViewController for this Part

  •   Transition Style is Page Curl (Interface builder)
  •   Double sided propery is false (Interface builder)
  •   Spine location can be min or max (Interface builder)
  •   Delegate and Data Source are nil
  •   Gesture based navigation is not included
  •   Total of 6 View Controllers for this example.
  •   Storyboard IDs in Interface Builder

Screen Shot 2018-06-17 at 1.18.50 AM.png

Delete the already present View Controller in the storyboard and drag out a UIPageViewController. Set it to Initial View Controller and reference it in identity inspector.

How to Use the project docs

The document contains useful content in Main storyboard file and in MainPageViewController.swift file.
Below is the implementation that is used to provide content to PageViewController.

struct GlobalSetup {
    static func getViewController(id: String) -> UIViewController! {
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: id)
    static var contentViewControllers = {
        return ["MyFirstViewController", "MySecondViewController", "MyThirdViewController", "MyFourthViewController", "MyFifthViewController", "MySixthViewController"].map({getViewController(id: $0)!})

So, as described above. We can create a structure that statically provide content to our PageViewController. We can add this structure as a top level declaration above our main class or write it down in a separate file.

Anyway, we can maintain global references to our view controllers through the struct (Global Setup).

The main function ​​contentViewControllers above maps array of String that contain StoryBoardIDs of View Controllers to an array of respective View Controllers. The storyboard IDs are defined in the interface Builder under identity inspector like so:

Screen Shot 2018-06-17 at 1.05.29 AM.png

Just like that, for each of the 6 view controllers in this example, we can set the storyboard ids to reference it programatically.

The contentViewControllers function has a helper method that returns a reference to an instantiated (allocated) view controller corresponding to the storyboard identifier sent as an argument.

Now, lets get to the meat and bones of this project,
our implementation UIPageViewController class:

This is a raw implementation of paging using no delegate or data source.

The implementation causes view controllers to transition when the user taps on screen. Spine location can be min/max and pages aren’t double sided.

import UIKit

class MainPageViewController: UIPageViewController {
    var currentPageIndexForMinMaxSpine = 0

    override func viewDidLoad() {

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

/**** ****                                  **** ****
 *  If spine location setting is Min or Max and double sided property is set to false
 *  The above mentioned properties can be setup from Interface Builder. UIPageViewController object's Attributes inspector
 **** ****                                 **** ****/
extension MainPageViewController {
    fileprivate func setupMinMaxSpinePageSet() {
        setupViewControllersMinMaxSpine(pageIndex: currentPageIndexForMinMaxSpine)
        let minmaxSpineTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(minmaxSpinePageTapped))
    fileprivate func setupViewControllersMinMaxSpine(pageIndex: Int) {
        self.view.isUserInteractionEnabled = false
        if pageIndex == GlobalSetup.contentViewControllers.count {
            self.currentPageIndexForMinMaxSpine = 0
            setViewControllers([GlobalSetup.contentViewControllers[0]], direction: .forward, animated: true, completion: {
                print("The success status is: \($0), We are on Page number \(self.currentPageIndexForMinMaxSpine + 1)")
                self.view.isUserInteractionEnabled = true
        } else {
            setViewControllers([GlobalSetup.contentViewControllers[pageIndex]], direction: .forward, animated: true, completion: {
                print("The success status is: \($0), We are on Page number \(pageIndex + 1)")
                self.view.isUserInteractionEnabled = true
    @objc func minmaxSpinePageTapped() {
        currentPageIndexForMinMaxSpine += 1
        setupViewControllersMinMaxSpine(pageIndex: currentPageIndexForMinMaxSpine)

We have to rely on setViewController function of UIPageViewController to provide content to it.

According to apple’s documentation :

If the transition style is UIPageViewController.TransitionStyle.pageCurl, the view controllers to pass in the viewControllers parameter depends on the spine location and the value of the isDoubleSided property:

In our case, we have isDoubleSided set to false and spine location can be min or max.

For such a setup, our setViewControllers demands one view controller (The one to be displayed upon tap).  We maintain the reference to the currently displayed View controller in our currentPageIndexForMinMaxSpine property. And when we reach the last view controller, we direct the paging flow to the first page upon tap.

The implementation and content providing using dataSource will be implemented in future part of this series.

Download the GitHub link

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s