Sotiris Kaniras Sotiris Kaniras - 11 months ago 123
iOS Question

Swift - Navigate through buttons between 3 different View Controllers in UIPageViewController

So I've been trying out UIPageViewController, where I have three different View Controllers (3 different classes)

So I've made it work! I can swipe from View Controller to View Controller, perfectly! At viewDidLoad of those three classes, I've made their self.view.tag = 0,1,2 respectively, so I know which my current View Controller is. Here's my RootViewController's code:

import UIKit

class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
@IBOutlet var bottomView: UIView!

var pageViewController: UIPageViewController!
var currentVCId = 0

override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.

pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
pageViewController.dataSource = self
pageViewController.delegate = self

let starterVC = viewControllerWith(id: 0)

pageViewController.setViewControllers([starterVC], direction: .forward, animated: false, completion: nil)
pageViewController.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)

self.view.bringSubview(toFront: bottomView)

pageViewController.didMove(toParentViewController: self)

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

func viewControllerWith(id: Int) -> UIViewController {
if id == 0 {
return self.storyboard?.instantiateViewController(withIdentifier: "FirstPageContentViewController") as! FirstPageContentViewController
else if id == 1 {
return self.storyboard?.instantiateViewController(withIdentifier: "SecondPageContentViewController") as! SecondPageContentViewController
else {
return self.storyboard?.instantiateViewController(withIdentifier: "RandomColorViewController") as! RandomColorViewController

func getViewControllerIdFor(previousViewController: Bool) -> Int? {
if previousViewController == true {
return currentVCId == 0 ? nil : currentVCId - 1

return currentVCId == 2 ? nil : currentVCId + 1

func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let returnIdValue = getViewControllerIdFor(previousViewController: true) {
return viewControllerWith(id: returnIdValue)

return nil

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let returnIdValue = getViewControllerIdFor(previousViewController: false) {
return viewControllerWith(id: returnIdValue)

return nil

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard completed else {

let viewController = pageViewController.viewControllers![0]
currentVCId = viewController.view.tag

@IBAction func goToA(_ sender: UIButton) {
if currentVCId != 0 {
let currentVC = pageViewController.viewControllers?[0]
let previusVC = pageViewController(pageViewController, viewControllerBefore: currentVC!)

pageViewController.setViewControllers([previusVC!], direction: .reverse, animated: true, completion: nil)
currentVCId = 0


The thing is, I've added 2 buttons to my RootViewController, to switch from View Controller to View Controller without swiping.. So, let's assume that I have already swiped to the second View Controller and I want to go back to my first one, by pressing the left button.. Remember that my first VC has already been loaded, so I can have modified its content..

So now, if I press the button, its code is the last method above, it will navigate back, but it will create a new instance of the desired VC! What I want to do, is to show my already loaded VC, as if I swiped... Like Snapchat...

Any idea on how to achieve that?

Answer Source

This is from the documentation on instantiateViewController(withIdentifier:)

This method creates a new instance of the specified view controller each time you call it.

Which is why you are getting new instances. I would recommend a different approach. Instead of letting the pageViewController keep track of the VCs and accessing them blindly by index and tag, why not store them as a property in your root? This would allow you to ensure the same instance of the VC, and would let you get away from checking for tags (which is dirty) to something more readable like checking for equality with the actual VC object.