xcode storyboard Container View - How do I access the viewcontroller
Asked Answered
R

5

50

I'm trying to use storyboard and get things working properly. I've added a a Container View to one of my existing views. When I try to add a reference to this in my view controller .h file (ctrl-drag), I get a IBOutlet UIView *containerView. How do I get a reference to the container view's view controller instead? I need the container view controller so I can set it's delegate to my view's controller so they can "talk" to each other.

I have my story board setup as:

enter image description here

And its referenced in my .h file as:

enter image description here

Notice in the .h that is is a UIView, not my InstallViewController for the view. How do I add a reference to the view controller? I need to be able to set its delegate.

Randyranee answered 7/11, 2012 at 17:38 Comment(1)
Here's the final, ultimate, last-word, maintained, tutorial on that! https://mcmap.net/q/17273/-how-to-add-a-subview-that-has-its-own-uiviewcontroller-in-objective-c There are some subtleties you need to know about, they are all explained there in detail and clearly.Meat
W
70

There is another solution by specifying an identifier for the embed segue(s) and retrieve the corresponding view controllers in method prepareForSegue:

The advantage of this way is that you needn't rely on a specific order in which your child view controllers are added due to the fact that each child view controller is embedded via an unique segue identifier.

Update 2013-01-17 - Example

- (void) prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    // -- Master View Controller
    if ([segue.identifier isEqualToString:c_SegueIdEmbedMasterVC])
    {
        self.masterViewController = segue.destinationViewController;
        // ...
    }
    // -- Detail View Controller
    else if ([segue.identifier isEqualToString:c_SegueIdEmbedDetailVC])
    {
        self.detailViewController = segue.destinationViewController;
        // ...
    }
}

c_SegueIdEmbedMasterVC & c_SegueIdEmbedDetailVC are constants with the corresponding ID of the segue IDs defined in the storyboard.

Worldwide answered 16/11, 2012 at 17:0 Comment(5)
Sorry for the late update. I'm sure you managed the problem by yourself in the meantime but eventually the example code helps other ones in the future.Worldwide
God answer. I Tried that trick before, and only after reading this answer I realized that it only works if you setup Segue Identifiers. However I would use isKindOfClass: instead.Songwriter
Why are there two view controllers (detail and master)? Isn't it just the one view controller in the container? What other segue are you referring to?Schnorrer
@shim: In my example project I had a kind of split view with a master and a detail view controller hosted in 2 separate container views - therefore the two view controllers.Worldwide
Its a shame that this is the best way of getting a reference to the child view controller. The whole point of the storyboards is to reduce boilerplate code like this. I could add the child view controller in viewDidLoad in fewer lines of code and it would be more maintainable than this. Anyone know if this fixed in XCode 6?Alexandra
N
14

When you add a container view the xcode calls the UIViewController method addChildViewController:

In your case, you can get the container ViewController looking for it on the SplashViewController's list of childViewControllers, something like this:

for (UIViewController *childViewController in [self childViewControllers])
{
    if ([childViewController isKindOfClass:[InstallViewController class]])
    {
        //found container view controller
        InstallViewController *installViewController = (InstallViewController *)childViewController;

        //do something with your container view viewcontroller

        break;
    }
}

I had the same doubt yesterday :)

Nynorsk answered 9/11, 2012 at 14:4 Comment(4)
I am trying to do something similar and what i don't get is the "vc" in InstallViewController *installViewController = (InstallViewController *)vc;Ambrogio
You are right, it should've been "childViewController". I've just fixed in the post. Sorry for taking that much to reply.Nynorsk
This approach feels a bit like a "workaround" to me. Doesn't it?Flex
Well, it did the trick :) Does the prepareForSegue method is called when a Container View is added to another view?Nynorsk
Z
2

The answer of Vitor Franchi is correct but could be more performant and convenient. Especially when accessing the child view controller several times.

Create a readonly property

@interface MyViewController ()
@property (nonatomic, weak, readonly) InstallViewController *cachedInstallViewController;
@end

Then create a convenient getter method

- (InstallViewController *)installViewController
{
    if (_cachedInstallViewController) return _cachedInstallViewController;

    __block InstallViewController *blockInstallViewController = nil;
    NSArray *childViewControllers = self.childViewControllers;
    [childViewControllers enumerateObjectsUsingBlock:^(id childViewController, NSUInteger idx, BOOL *stop) {

        if ([childViewController isMemberOfClass:InstallViewController.class])
        {
            blockInstallViewController = childViewController;
            *stop = YES;
        }
    }];

    _cachedInstallViewController = blockInstallViewController;

    return _cachedInstallViewController;
}

From now on access the child view controller that way

[self.installViewController doSomething];
Zed answered 31/3, 2014 at 10:53 Comment(0)
A
0
UIView* viewInsideOfContainer = installerView.subviews[0];

Will give you the UIView inside of the UIViewController that your controller UIView references. You can cast the subview to any type that inherits from UIView.

Ailbert answered 17/5, 2013 at 17:37 Comment(2)
I'm not sure I like making that assumption, but it's useful to know.Cryptoanalysis
That's not the universal way: view hierarchy can change and this method won't work.Theatre
C
0

If the nib is loaded it will call addChildViewController as part of the initialisation process

so a performant solution could be also to overwrite

- (void)addChildViewController:(UIViewController *)childController

there you can catch your childController e.g. by comparing its Class and assign it to a property / ivar

-(void)addChildViewController:(UIViewController *)childController
{
    [super addChildViewController:childController];

    if([childController isKindOfClass:[InstallViewController class]])
    {
        self.installViewController = (InstallViewController *)childController;
    }

}

This will save your from iterating trough the childViewControllers.

Callean answered 31/3, 2014 at 11:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.