Search code examples
iosswiftviewcontrolleruipageviewcontroller

how to be back to previous VC or next VC from a view controller inside UIPageViewController?


I have 3 view controllers that controlled by UIPageViewController like the image below

enter image description here https://i.sstatic.net/9Lqoj.png

if I swipe the screen, it will move from RedVC to BlueVC and to YellowVC.

I want make next button and back button in the BlueVC. so if the user press the back button, it will go to RedVC, and if the user press the next button, it will go to YellowVC. how to do that ?

here is my code in the root page view controller:

import UIKit

class RootPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    lazy var viewControllerList : [UIViewController] = {
        let storyBoard = UIStoryboard(name: "Main", bundle: nil)

        let redVC = storyBoard.instantiateViewController(withIdentifier: "redVC")
        let blueVC = storyBoard.instantiateViewController(withIdentifier: "blueVC")
        let yellowVC = storyBoard.instantiateViewController(withIdentifier: "yellowVC")

        return [redVC,blueVC,yellowVC]
    }() 


    // to add dot indicator
    var pageControl = UIPageControl()




    override func viewDidLoad() {
        super.viewDidLoad()


        self.dataSource = self
        self.delegate = self

        configurePageControl()
        turnIntoPage()

    }




    func turnIntoPage() {
        // set view controllers to be pages

        if let firstViewController = viewControllerList.first {
            // if so then set the direction
            self.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
        }
    }




    // MARK: - Page View Controller Data Source functions
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {

        // a method that will handle view controller before current view controller

        // make precaution , if the lodic is wrong then return nil (prevent index out of logic)
        guard let vcIndex = viewControllerList.index(of: viewController) else { return nil}
        let previousIndex = vcIndex - 1
        guard previousIndex >= 0 else { return nil}
        guard viewControllerList.count > previousIndex else {return nil}


        // return the value
        return viewControllerList[previousIndex]
    }


    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

        // a method that will handle view controller after current view controller

        // make precaution , if the lodic is wrong then return nil (prevent index out of logic)
        guard let vcIndex = viewControllerList.index(of: viewController) else { return nil}
        let nextIndex = vcIndex + 1
        guard viewControllerList.count != nextIndex else {return nil}
        guard viewControllerList.count > nextIndex else {return nil}

         // return the value
        return viewControllerList[nextIndex]


    }




     // to add dot indicator


    // MARK: Page View Controller Delegate functions
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        let pageContentViewController = pageViewController.viewControllers![0]
        self.pageControl.currentPage = viewControllerList.index(of: pageContentViewController)!
    }


    func configurePageControl() {

        // The total number of pages that are available is based on how many available colors we have.
        pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50))
        self.pageControl.numberOfPages = viewControllerList.count
        self.pageControl.currentPage = 0
        self.pageControl.tintColor = UIColor.black
        self.pageControl.pageIndicatorTintColor = UIColor.white
        self.pageControl.currentPageIndicatorTintColor = UIColor.black
        self.view.addSubview(pageControl)
    }




}

Solution

  • So, in your RootPageViewController, you could have a function there that will use setViewControllers.

    And that setViewControllers is described as:

    // Set visible view controllers, optionally with animation. Array should only include view controllers that will be visible after the animation has completed. // For transition style 'UIPageViewControllerTransitionStylePageCurl', if 'doubleSided' is 'YES' and the spine location is not 'UIPageViewControllerSpineLocationMid', two view controllers must be included, as the latter view controller is used as the back.

    So let's try to have a Next function.

    func next() {
        let vcs = self.viewControllerList
        //or self.viewControllers
        let currentPage = self.pageControl.currentPage
        let nextPage = currentPage + 1
    
        if nextPage < vcs.count {
            let nextVC = vcs[nextPage]
            self.setViewControllers([nextVC], direction: .forward, animated: true) { _ in
                self.pageControl.currentPage = nextPage
            }
        }
    }
    

    As you can see, we are safely getting a viewController from the vcs you made in your code, which contains all the pages. And after we get the next viewController, if there's any, we use the setViewControllers. To make it better, we're setting the currentPage of your pageControl to tell which circle at the bottom of your pageController to be colored.

    And then from your BlueVC, get a reference of your pageController. How? By accessing the self.parent and cast it as your RootPageViewController. Like so:

    @IBAction func next(_ sender: Any) {
        if let parentVC = self.parent as? RootPageViewController {
            parentVC.next()
        }
    }
    

    Now, it's your task to implement the previous function.