Access Container View Controller from Parent iOS

How can I access data from a container view from the parent view controller in swift?

To gain access to the container view from your parent, you will have to pass a reference from the container view to the parent and then use it in the parent view controller, there are many ways to do that and here is one of them.

In your viewDidAppear of InfoRegisterController which is the container view controller add the following code, this method will get a reference of InfoRegisterController into the parent to be used.

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

let signUpControllerParent = self.parent as! SignUpController
signUpControllerParent.saveContrainerViewRefference(vc: self)

}

Now in SignUpController add a local variable for the coming reference to be saved and used later to get the data from the textfields.

var infoRegisterRefferenceVC : InfoRegisterController?

Add this method also in your parent SignUpController

func saveContrainerViewRefference(vc:InfoRegisterController){

self.infoRegisterRefferenceVC = vc

}

Now you can have access to all the textfields and the methods in the container view from the parent for example:

var fullNameTextField = self.infoRegisterRefferenceVC.fullName.text 

This should be it :)

Pass values from container view to parent view controller

Steps to do that.

  1. Your container view must contain a embed segue to the child view controller, name that segue something like this.... "homeToContainer" (See the attached Image)
    Add Embed segue name

  2. Add this method to your parent View controller (DiaryEntryViewController )

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let vc = segue.destination as? ChildViewController,
    segue.identifier == "homeToContainer" {
    vc.delegate = self
    }
    }
  3. Add Protocol and its variable in ChildViewController:

        protocol ChildToParentProtocol:class {


    func buttonClickedByUser()
    func needToPassInfoToParent(with value:Int)

    }


    class ChildViewController: UIViewController {

    weak var delegate:ChildToParentProtocol? = nil

    @IBAction func createTourPressed(_ sender: UIButton) {
    // Call here delegate methods to tell parent about the action
    delegate?.buttonClickedByUser()

    }

    }
  4. In the last in your parent ViewController, add this Extension:

            extension DiaryEntryViewController:ChildToParentProtocol {

    func buttonClickedByUser() {

    }
    func needToPassInfoToParent(with value:Int) {


    }
    }

Access Container View Controller from Parent iOS

Yes, you can use the segue to get access the child view controller (and its view and subviews). Give the segue an identifier (such as alertview_embed), using the Attributes inspector in Storyboard. Then have the parent view controller (the one housing the container view) implement a method like this:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString * segueName = segue.identifier;
if ([segueName isEqualToString: @"alertview_embed"]) {
AlertViewController * childViewController = (AlertViewController *) [segue destinationViewController];
AlertView * alertView = childViewController.view;
// do something with the AlertView's subviews here...
}
}

Call parent View controller function from container view

If you have embedded view controller on another view controller, you can message it's parent view controller with many ways.

  1. In child (embedded) view controller, you can have reference to parent. Give identifier to the embedding segue in the storyboard, declare var with type of the parent in child, and assign the parent on prepare(for segue: UIStoryboardSegue, sender: Any?) function, and call any public methods of the parent.
  2. Create protocol, make the parent confirm to the protocol, create var of the protocol in child, set parent as protocol object of the child in prepare(for segue: UIStoryboardSegue, sender: Any?), and call protocol methods whenever you want.
  3. Just post notification to NotificationCenter on child, and observe the notification on parent. Documentation of NotificationCenter, here you can find a brief example of using it.

Hope this helps!

PS: You can also observe properties of the child in the parent vc, if you want that way, I can explain.

From within a view controller in a container view, how do you access the view controller containing the container?

You can use the prepareForSeguemethod in Vc1 as an embed segue occurs when the ContainerViewController is made a child. you can pass self as an obj or store a reference to the child for later use.

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString * segueName = segue.identifier;
if ([segueName isEqualToString: @"embedseg"]) {
UINavigationController * navViewController = (UINavigationController *) [segue destinationViewController];
Vc2 *detail=[navViewController viewControllers][0];
Vc2.parentController=self;
}
}

Edit: minor code fix

How to pass data from parent view controller to child container view controller

You can try it more early like

let childVC = storyboard!.instantiateViewController(withIdentifier: "ChildVC") as! ChildVC
childVC.boolVariable = true

Accessing parent view buttons in container view controller

One option is to use NotificationCenter, have your buttons post the notification, and have your child view controller listen for them.

For example, in the parent VC, post the notification in the function called when a button is tapped, like so:

@IBAction func buttonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ButtonTapped"), object: nil, userInfo: nil)
}

In the child VC that needs to respond to the button tap, place the following code in viewWillAppear: to set the VC as a listener for that specific notification:

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(handleButtonTap(_:)), name: NSNotification.Name(rawValue: "ButtonTapped"), object: nil)
}

In the same view controller, add the handleButtonTap: method mentioned above. When the "ButtonTapped" notification comes in, it will execute this method.

@objc func handleButtonTap(_ notification: NSNotification) {
//do something when the notification comes in
}

Don't forget to remove the view controller as an observer when it is no longer needed, like this:

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}

Container view function not calling in parent view

To call functions, or access properties, in a View Controller embedded in a Container View, you need to get and keep a reference to that controller.

When the Container View loads the embedded VC, it calls Prepare For Segue. Grab your reference there:

class WithContainerViewController: UIViewController {

var routeSelectionVC: RouteSelectionViewController?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}

@IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}

}

class RouteSelectionViewController: UIViewController {

@IBOutlet weak var loadingCoverView: UIActivityIndicatorView!

override func viewDidLoad() {
super.viewDidLoad()
}

//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}

}

You will likely next ask about calling a function in the "parent" VC from the embedded VC. This can be done with protocol / delegate pattern, or with closures. Again, you can set that up in prepare for segue:

class WithContainerViewController: UIViewController {

var routeSelectionVC: RouteSelectionViewController?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// set the closure in the VC embedded in Container View
vc.callbackClosure = {
self.routeSelectionButtonTapped()
}
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}

@IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}

func routeSelectionButtonTapped() -> Void {
print("Button in RouteSelectionViewController in Container View was tapped!")
}

}

class RouteSelectionViewController: UIViewController {

@IBOutlet weak var loadingCoverView: UIActivityIndicatorView!

var callbackClosure: (() -> ())?

override func viewDidLoad() {
super.viewDidLoad()
}

//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}

@IBAction func didTap(_ sender: Any) {
callbackClosure?()
}

}

How to access the child view controller from parent view controller in swift?

The answer is ... (drum-roll......) ... it depends.. :-)

NSViewController & UIViewController have a .parent property.
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621362-parent
https://developer.apple.com/documentation/appkit/nsviewcontroller/1434491-parent

However, it is only populated if you're implementing the container-view-controller design pattern: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html (many uikit components use this pattern) IE:

aViewController.addChild(bViewController)
bViewControler.didMove(toParent: aViewController)
bViewController.parent // is aViewController

*Note: you'll have to manage the view stack yourself, this just ensures things like the lifecycle/drawing/resize/etc handlers happen in the correct order

If you need to retain a reference to a view controller (child or otherwise), create a property on the child and set a reference to it, or use the container-view-controller design, it's very useful.

Cheers

- J



Related Topics



Leave a reply



Submit