Search code examples
iosswiftuipageviewcontroller

Swift - Change UIPageViewController view on button click


I'm using an UIPageViewController and my intention is to switch the current view on click of a UIButton in one of its UIViewController. I've already googled my problem and found some similar threads, but I couldn't make head nor tail of the answers.

For instance, the button in my second UIViewController vc2 shall change view to vc3.

RootPageViewController

import UIKit

class RootPageViewController: UIPageViewController, UIPageViewControllerDataSource {

    lazy var viewControllerList:[UIViewController] = {

        let sb = UIStoryboard(name: "Main", bundle: nil)

        let vc1 = sb.instantiateViewController(withIdentifier: "timelineView")
        let vc2 = sb.instantiateViewController(withIdentifier: "mainView")
        let vc3 = sb.instantiateViewController(withIdentifier: "addView")

        return [vc1, vc2, vc3]
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self

        let secondViewController = viewControllerList[1]
        self.setViewControllers([secondViewController], direction: .forward, animated: false, completion: nil)

    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }

        let previousIndex = vcIndex - 1
        guard previousIndex >= 0 else { return nil }

        guard viewControllerList.count > previousIndex else { return nil }

        return viewControllerList[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }

        let nextIndex = vcIndex + 1

        guard viewControllerList.count != nextIndex else { return nil }

        guard viewControllerList.count > nextIndex else { return nil }

        return viewControllerList[nextIndex]
    }

}


Button inside of second ViewController

@IBAction func addData(_ sender: Any) {


    }

Solution

  • In RootPageViewController's viewDidLoad add second view controller button's target to self and add addData method to RootPageViewController. You can use setViewControllers and move to the last view controller in that method

    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.dataSource = self
        if let secondViewController = viewControllerList[1] as? SecondViewController {
            self.setViewControllers([secondViewController], direction: .forward, animated: false, completion: nil)
            secondViewController.button.action = #selector(addData(_:))
        }
    }
    @IBAction func addData(_ sender: Any) {
        if let thirdViewController = viewControllerList.last {
            self.setViewControllers([thirdViewController], direction: .forward, animated: false, completion: nil)
        }
    }
    

    OR

    Keep addData method in MainViewControlle and add a protocol to it like this

    protocol MainVCDelegate {
        func buttonPlusTapped()
    }
    class MainViewController: UIViewController {
    
        @IBOutlet var buttonPlus: UIBarButtonItem!
    
        var delegate: MainVCDelegate?
        @IBAction func addData(_ sender: Any) {
            delegate?.buttonPlusTapped()
        }
    }
    

    In RootPageViewController confirm to this delegate and add the delegate method

    class RootPageViewController: UIPageViewController, UIPageViewControllerDataSource, MainVCDelegate {
    
        func buttonPlusTapped() {
            if let thidViewController = viewControllerList.last {
                self.setViewControllers([thidViewController], direction: .forward, animated: false, completion: nil)
            }
        }
    
        lazy var viewControllerList:[UIViewController] = {
    
            let sb = UIStoryboard(name: "Main", bundle: nil)
    
            let vc1 = sb.instantiateViewController(withIdentifier: "timelineView")
            let vc2 = sb.instantiateViewController(withIdentifier: "mainView") as! MainViewController
            vc2.delegate = self
            let vc3 = sb.instantiateViewController(withIdentifier: "addView")
            return [vc1, vc2, vc3]
        }()
    }