6
votes

I have a view controller (containing my menu) presented on top of another view controller (my application).

I would need to access the presenting view controller (below my menu) from the presented view controller (my menu), for example to access some variables or make the presenting view controller perform one of its segues.

However, I just can't figure out how to do it. I'm aware of the "presentingViewController" and "presentedViewController" variables but I didn't manage to use them successfully.

Any Idea ?

Code (from the presented VC, which as a reference to the AppDelegate in which the window is referenced) :

if let presentingViewController = self.appDelegate.window?.rootViewController?.presentingViewController {
    presentingViewController.performSegue(withIdentifier: "nameOfMySegue", sender: self)
}
2
A common used pattern to solve this issue is to use the delegate design pattern. Declare a delegate in your Menu VC and set the first VC as the delegate. Now you can set a property or a call a method on the delegate. Another way could be to use the notification pattern.TheAppMentor
@TheAppMentor describes two better design patterns over directly referencing the presented view controller. However, Apple does provide the presentingViewController if you want a strongly coupled relationship.dmorrow
Both methods actually work. Thanks a lot.vbuzze

2 Answers

11
votes

Here is a use of the delegation Design pattern to talk back to the presenting view controller.

First Declare a protocol, that list out all the variables and methods a delegate is expected to respond to.

protocol SomeProtocol {

    var someVariable : String {get set}

    func doSomething()
}

Next : Make your presenting view controller conform to the protocol. Set your presenting VC as the delegate

class MainVC: UIViewController, SomeProtocol {

    var someVariable: String = ""

    func doSomething() {
        // Implementation of do
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Your code goes here.

        if let destVC = segue.destination as? SubVC{
            destVC.delegate = self
        }
    }

}

Finally, when you are ready to call a method on the presenting VC (Delegate).

class SubVC: UIViewController {
    var delegate : SomeProtocol?


    func whenSomeEventHappens() {

        // For eg : When a menu item is selected

        // Set some Variable
        delegate?.someVariable = "Some Value"

        // Call a method on the deleate
        delegate?.doSomething()
    }

}
5
votes

Assuming that VCApplication is presenting VCMenu, in VCMenu you can access VCApplication with:

weak let vcApplication = self.presentingViewController as? VCApplicationType

Your example self.appDelegate.window?.rootViewController?.presentingViewController is looking for the ViewController that presented the rootViewController - it will be nil.

EDIT Per TheAppMentor I've added weak so there are no retain cycles.