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

IosIpadViewStoryboardContainers

Ios Problem Overview


This is tricky to word but I have a view controller (vc1) that contains a container view (I'm using storyboards). Within that container view is a navigation controller and a root view controller (vc2).

From within the vc2 how can I get access to vc1?

Or, how do I pass vc1 to vc2? (baring in mind that I'm using storyboards).

Ios Solutions


Solution 1 - Ios

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

Solution 2 - Ios

To access parent view controller from within your child view controller you must override didMoveToParentViewController:

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    //Use parent
}

On Xcode Command+Click over this method for more info:

> These two methods are public for container subclasses to call when transitioning between child controllers. If they are overridden, the overrides should ensure to call the super. The parent argument in both of these methods is nil when a child is being removed from its parent; otherwise it is equal to the new parent view controller.

> addChildViewController: will call [child willMoveToParentViewController:self] before adding the child. However, it will not call didMoveToParentViewController:. It is expected that a container view controller subclass will make this call after a transition to the new child has completed or, in the case of no transition, immediately after the call to addChildViewController:. Similarly removeFromParentViewController: does not call [self willMoveToParentViewController:nil] before removing the child. This is also the responsibilty of the container subclass. Container subclasses will typically define a method that transitions to a new child by first calling addChildViewController:, then executing a transition which will add the new child's view into the view hierarchy of its parent, and finally will call didMoveToParentViewController:. Similarly, subclasses will typically define a method that removes a child in the reverse manner by first calling [child willMoveToParentViewController:nil].

Solution 3 - Ios

You can use delegation using the same method Bonnie used. Here is how you do it:

In your containerViews ViewController:

class ContainerViewViewController: UIViewController {
   //viewDidLoad and other methods

   var delegate: ContainerViewControllerProtocol?

   @IBAction func someButtonTouched(sender: AnyObject) { 
    self.delegate?.someDelegateMethod() //call this anywhere
   }

}

protocol ContainerViewControllerProtocol {
    func someDelegateMethod()
}

In your parent ViewController:

class ParentViewController: UIViewController, ContainerViewControllerProtocol {
   //viewDidLoad and other methods

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "filterEmbedSegue" {
            let containerViewViewController = segue.destinationViewController as ContainerViewViewController
        
            containerViewViewController.delegate = self
        }
    }

    func someDelegateMethod() {
        //do your thing
    }
}

Solution 4 - Ios

Use property parentViewController as self.parentViewController

Solution 5 - Ios

Thank you Bonnie for telling me what to do. Indeed the prepare for segue method is the way to go.

I'm just clarifying the code and steps here.

So first off, name the segue(link) in the storyboard that connects the container view to its first view controller. I named mine "toContainer".

Then in the view controller containing the container view add this method

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString: @"toContainer"]) {
        UINavigationController *navViewController = (UINavigationController *) [segue destinationViewController];
        UIViewController *vc2 = [navViewController viewControllers][0];
    }
}

So vc2 was the controller I wanted to get reference to.

This worked for me, your method would be slightly different inside the prepareForSegue if your first viewconroller wasn't a navigation controller.

Solution 6 - Ios

  1. on VC2 expose a property for passing in a reference to VC1

    //VC2.h #import "VC1.h"

    @interface VC2 : NSObject @property (strong, nonatomic) VC1 *parent; @end

  2. on VC1, pass self into the property exposed in VC2 in your prepareForSegue method after you setup your segue's identifier to "ToVC2". Then pass the reference like so:

    //VC1.m @implementation VC1

    • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([segue.identifier isEqualToString:@"ToVC2"]) { VC2 *vc2 = segue.destinationViewController; vc2.parent = self; } }

Solution 7 - Ios

Swift - An alternative is to create a reference in parent UIViewController (vc1) to child/subview UIViewController (vc2) and in vc2 to vc1. Assign the references in parent(vc1) viewDidLoad() example below.

Parent UIViewController vc1:

      class vc1: UIViewController {
          
          @IBOutlet weak var parentLabel: UILabel!
          var childVc2: vc2?;
               
           overide func viewDidLoad() {
               super.viewDidLoad();
               // Use childViewControllers[0] without type/class verification only 
               // when adding a single child UIViewController 
               childVc2 = self.childViewControllers[0] as? vc2;
               childVc2?.parentVc1 = self
           }
      }

Child UIViewController vc2:

      class vc2: UIViewCortoller {
          var parentVc1: vc1?;
          
          // At this point child and parent UIViewControllers are loaded and 
          // child views can be accessed
          override func viewWillAppear(_ animated: Bool) {
             parentVc1?.parentLabel.text = "Parent label can be edited from child";
          }
      } 

In the Storyboard remember to set in Identity Inspector the parent UIViewContoller class to vc1 and child UIViewContoller class to vc2. Ctrl+drag from Container View in vc1 UIViewController to vc2 UIViewController and select Embed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMark BridgesView Question on Stackoverflow
Solution 1 - IosBonnieView Answer on Stackoverflow
Solution 2 - IosFirulaView Answer on Stackoverflow
Solution 3 - IososrlView Answer on Stackoverflow
Solution 4 - IosBorzhView Answer on Stackoverflow
Solution 5 - IosMark BridgesView Answer on Stackoverflow
Solution 6 - IossmileBotView Answer on Stackoverflow
Solution 7 - IosMattView Answer on Stackoverflow