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

38,306

Solution 1

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

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

Use property parentViewController as self.parentViewController

Solution 4

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 5

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.

Share:
38,306
Mark Bridges
Author by

Mark Bridges

I am a software engineer, primarily focused on Swift & Objective-C iOS apps. I have worked in tech for over 10 years, with 8 years spent as an iOS developer. I have released over 30 apps myself & accumulated well over a million downloads. I have created apps for all of Apple’s platforms iOS, watchOS, tvOS & macOS. I have a degree with honours in Computing from the Open University.

Updated on July 09, 2022

Comments

  • Mark Bridges
    Mark Bridges almost 2 years

    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).

  • Bonnie
    Bonnie over 10 years
    @Firula the line UINavigationController * navViewController = (UINavigationController *) [segue destinationViewController]; does not initialize anything its only a pointer to the already initialized Controller.
  • Bonnie
    Bonnie over 10 years
    you could have marked my answer as correct, and added yours in comments for better clarification.
  • Fattie
    Fattie about 10 years
    Mark, you need to mark Firula's or Bonnie's answer as correct, and go ahead and edit your question and put this useful information in the question.
  • Mark Bridges
    Mark Bridges about 10 years
    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]; } } vc2 was the controller I wanted to get reference to.
  • BananaAcid
    BananaAcid almost 9 years
    note: destinationViewController was already my containers embedded target UIViewController
  • Craig Watkinson
    Craig Watkinson over 8 years
    In my case this didn't work as self.parentViewController was nil. I added the containerView in a storyboard, then set the associated view controller class to be my particular subclass. I expected parentViewController to be set to the VC that contained the container view, but this isn't getting set automagically. I had to use the -prepareForSegue solution above
  • Chucky
    Chucky about 8 years
    This is very helpful to me, thank you. One related question: Why would my child view controller's ViewDidLoad method get called before the parent one? Have I messed something up with the set up of the container?
  • Bonnie
    Bonnie about 8 years
    @Chucky to understand this, imagine the Child is essentially a part(uicontrol) of the Parent and parents -(void)viewDidLoad is incomplete unless all the UIElements are loaded. Therefore the Child's -(void)viewDidLoad it is supposedly called before the parents.
  • Jenita _Alice4Real
    Jenita _Alice4Real over 7 years
    but what if i have two other segues from the view controller vc2. how do i access vc1 from them?
  • Travis M.
    Travis M. almost 7 years
    property is "self.parent" in swift 3
  • malhal
    malhal over 5 years
    its not good practice for a child to call method on its parent using that reference you passed in. You should instead pass the data into the child that it needs and use the delegate pattern for callbacks.