Search code examples
iosswiftuiviewcontrolleruisegmentedcontrol

Trying to check if a view controller is loaded, and then presenting that within a container (UISegmentedController)


I'm trying to navigate view controllers using a segmented controller. The child VC gets attached to the parent and I can switch VC's this way. However, every time I go back to a segment the VC gets reinstated all over again. How can I make the VC attach itself again if it's already loaded into memory?

Here is my code and how I'm trying to check if a view is loaded.

@objc func changeView1(_ kMIDIMessageSendErr: Any?) {
    let childViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "View1")

    if childViewController.isViewLoaded == true {
        childViewController.didMove(toParentViewController: self)
        NSLog("ViewIsLoaded1")
    } else if childViewController.isViewLoaded == false {
        self.addChildViewController(childViewController)
        self.view.addSubview(childViewController.view)
        childViewController.didMove(toParentViewController: self)
        NSLog("ViewIsLoaded2")
    }
}

@objc func changeView2(_ kMIDIMessageSendErr: Any?) {
    let childViewController2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "View2")

    if childViewController2.isViewLoaded == true {
        childViewController2.didMove(toParentViewController: self)
        NSLog("ViewIsLoaded3")
    } else if childViewController2.isViewLoaded == false {
        self.addChildViewController(childViewController2)
        self.view.addSubview(childViewController2.view)
        childViewController2.didMove(toParentViewController: self)
        NSLog("ViewIsLoaded4")
    }
}

I am able to change VC's using the segmented control, but I don't want to reload the VC every time I change segments.


Solution

  • There are a number of problems with your code:

    1) You're creating a brand new instance of your child controller every time either of these functions is run. Therefore isViewLoaded is always false, and the execution flows into your else block every single time.

    2) The else if is not required.

    3) Providing the above are solved, you shouldn't be calling didMove(toParentViewController:) to switch back. You should simply be hiding and unhiding the views.

    The solutions to this are as follows:

    1) Assign a reference to your child controllers as an instance variable, and create a child only if that reference is nil.

    2) Instead of checking isViewLoaded, check your instance variable reference to see if it is nil.

    3) Remove the if part of your else clause - it's redundant.

    4) Inside your if code block, simply hide and unhide the appropriate views using isHidden.

    Here's an example implementation:

    private var firstChild: UIViewController?
    private var secondChild: UIViewController?
    
    @objc func changeView1(_ kMIDIMessageSendErr: Any?) {
    
        if firstChild != nil {
            firstChild?.view.isHidden = false
            secondChild?.view.isHidden = true
        } else {
            firstChild = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "View1")
            self.addChildViewController(firstChild!)
            self.view.addSubview(firstChild!.view)
            firstChild!.didMove(toParentViewController: self)
        }
    }
    
    @objc func changeView2(_ kMIDIMessageSendErr: Any?) {
    
        if secondChild != nil {
            firstChild?.view.isHidden = true
            secondChild?.view.isHidden = false
        } else {
            secondChild = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "View2")
            self.addChildViewController(secondChild!)
            self.view.addSubview(secondChild!.view)
            secondChild!.didMove(toParentViewController: self)
        }
    }