Search code examples
iosswiftxcodeuipageviewcontroller

How do I pass data from a UIPageViewController to one of its "subviews"?


Ok, so here's my issue:

I want to pass the value of a variable of type string through multiple view controllers and into one of the pages in my UIPageViewController. Here is the code for the UIPageViewController (starts with the import UIKit right below):

import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    var recieveProdTime2 = String()     // 2nd view controller for productive time data to pass through

    lazy var orderedViewControllers: [UIViewController] = {
        return [self.newVC(viewController: "sbHome"), self.newVC(viewController: "sbPoints"), self.newVC(viewController: "sbShop")]
    }()

    var pageControl = UIPageControl()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self
        if let firstViewController = orderedViewControllers.first {
            setViewControllers([firstViewController],
                               direction: .forward,
                               animated: true,
                               completion: nil)
        }

        self.delegate = self
        configurePageControl()

        if recieveProdTime2 != "" {
            performSegue(withIdentifier: "segue3", sender: self)
        }

    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let destinationVC = segue.destination as! HomeViewController
        destinationVC.productive = recieveProdTime2
    }

    func configurePageControl() {

        pageControl = UIPageControl(frame: CGRect(x: 0, y: UIScreen.main.bounds.maxY - 50, width: UIScreen.main.bounds.width, height: 50))
        pageControl.numberOfPages = orderedViewControllers.count
        pageControl.currentPage = 0
        self.view.addSubview(pageControl)

    }

    func newVC(viewController: String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
    }

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

        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }

        let previousIndex = viewControllerIndex - 1

        guard previousIndex >= 0 else {
            return orderedViewControllers.last
        }

        guard orderedViewControllers.count > previousIndex else {
            return nil
        }

        return orderedViewControllers[previousIndex]

    }

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

        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }

        let nextIndex = viewControllerIndex + 1

        guard orderedViewControllers.count != nextIndex else {
            return orderedViewControllers.first
        }

        guard orderedViewControllers.count > nextIndex else {
            return nil
        }

        return orderedViewControllers[nextIndex]

    }

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

        let pageContentViewController = pageViewController.viewControllers![0]
        self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
    }

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

}

As you can see, I am instantiating 3 different views. I want to pass the value of recieveProdTime2 into HomeViewController, which is one of the views in the UIPageViewController. Here is the code for HomeViewController:

import UIKit

class HomeViewController: UIViewController {

    @IBOutlet weak var focusSession: UILabel!
    @IBOutlet weak var breakSession: UILabel!

    var productive = String()   // Productive time data from ViewController.swift arrives and is store

    override func viewDidLoad() {
        super.viewDidLoad()

        focusSession.text = productive

    }

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

}

I use the "prepare(for segue:...)" method in the PageViewController class and call on the variable named "productive" in the HomeViewController class to set the text of the label named "focusSession" in that class.

The Problem

Everything runs smoothly, there are no errors, and the project builds and runs....but the text of the label is empty!...It just appears as an empty label in the simulator! I check the error box, and I see this message: "Warning: Attempt to present Focus.HomeViewController on Focus.PageViewController whose view is not in the window hierarchy!"

How can I send data from the PageViewController to the HomeViewController and properly set the text of my label???


Solution

  • Try this:

    replace your orderedViewControllers initialization with:

    lazy var orderedViewControllers: [UIViewController] = {
        let sbPoint = self.newVC(viewController: "sbPoints") as! HomeViewController
        if recieveProdTime2 != "" {
            sbPoint.productive = recieveProdTime2
        }
        return [self.newVC(viewController: "sbHome"), sbPoint , self.newVC(viewController: "sbShop")]
    }()
    

    And enter some value in recieveProdTime2. Here you do not need to use prepareForSegue so remove:

    if recieveProdTime2 != "" {
        performSegue(withIdentifier: "segue3", sender: self)
    }
    

    and

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let destinationVC = segue.destination as! HomeViewController
        destinationVC.productive = recieveProdTime2
    }