简体   繁体   中英

View Controller Containment when using Interface Builder

I'm attempting define a series of container view controllers using Interface Builder (probably my first mistake). In the storyboard, I created the top view controller, added 3 container views, which automatically added each child view controller to the storyboard. I added an outlet for each container view, and am able to successfully maneuver through the child views by hiding/showing the container views. There's really not much code to speak of, it's just:

-(IBAction) button1Pushed:(id)sender
{
     containerView2.hidden = true;
     containerView1.hidden = false;
}

While that works, I need to update the content on each showing. viewWillAppear (and related functions) fire for the Child View Controllers only on the initial creation, not when hiding/showing the containers. I supposed I could add something like:

    [childVC1 updateContent];
    containerView1.hidden = false;

but I was hoping I could rely on viewWillAppear (and related functions). I've tried a few things with limited success, and have several questions:

  1. I understand the example in Apple's Programming Guide for manually creating containers:
  [self addChildViewController:content]; // 1 [self.view addSubview:self.currentClientView]; [content didMoveToParentViewController:self]; // 3 

but how does that apply when using IB? There's no mention of IB in that Programming Guide. IB must be calling addChildViewController, because I can find all the the container VCs using [self childViewControllers]. But since viewWillAppear only happens on creation, does that mean IB didn't go on to add the views as well?

So question 1 is: when adding container views via Interface Builder, how much of the example code does IB handle and how much do I have to implement?

  1. As an experiment, I added this code in the parent's viewDidLoad:
 for ( UIViewController *vc in [self childViewControllers]) { [self.view addSubview:vc.view]; [vc didMoveToParentViewController:self]; } 

That caused each of the child views to all appear at once on top of each other at the top left of the screen. It no longer respects the IB layout, and unsurprisingly, no longer respects showing/hiding the containerView, either. I can control them with

 vc.view.hidden = true;

but they're moved to some default position. I could manually reset their coordinates, but one of the reasons for using IB in the first place was to avoid doing screen positioning via code.

question #2 is: when adding container views in IB, how am I supposed to manage displaying the children while still honoring the IB layout? By manipulating the container view's outlet? by manipulating the child view controller (found via [self childViewControllers])? Or something else?

At the end of the day, my desired state is:

  1. Define the screen layouts and positions in IB for the parent and children
  2. Hide/show children based on buttons selected by the user
  3. viewWillAppear fires each time a child is displayed, allowing me to update the content

Any advice on reaching this state is appreciated, thanks!

Answer 1: When you add child view controllers via the storyboard (called container views in this case) then the children are added via segues. No it's not a segue in the normal sense where you push or present a view controller, but the method that's called in your 'super' view controller is prepareForSegue. In interface builder you can name the connection between the two controllers just like a normal segue and grab a reference to the view controller in this instance. This takes the places of the whole dance of addChildViewController, didMoveToParent and so forth.

Answer two: I think it makes sense to address your desired state. I looks like you have already solved #1 and #2. As for getting viewWillAppear to fire again you would have to get your parents viewWillAppear to fire again by doing something like pushing and popping a new VC or presenting and dismissing one. Just because you set it to hidden and then unhide it won't make it fire again (hidden basically tells the drawing system to not render it).

The recommended approach (from Apple, forgive me I don't know the link) is to do all of your updates in your view subclass via updateViewContraints or layoutSubviews. In the view controller world the similar method is ViewDidLayoutSubviews. You can signal to your view that it needs to be laid out again by calling [yourViewController.view setsNeedsLayout]. For reference check out the doc on UIView in regards to needsLayout.

Likely what you'll want to do when your button is pressed to unhide your viewController is something like:

1: set any properties or call methods on your viewController which update its content

2: call setsNeedsLayout on your viewControllers view

3: unhide it.

viewWillAppear will not fire for the children each time they are shown. Adjust your thinking.

When you create a container view and a child view in IB, it sets up an embed segue to link to the child view controllers.

What I suggest you do is to add unique segue identifiers for each of your child view controllers, and then in prepareForSegue, use an if/else if/else if statement to match each segue ID. When you find a segue Id, save that child view controller in a property of the parent:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
  if ([segue.identifier isEqualToString: @"firstChildID")
    self.firstChildVC =   (FirstChildVC *) segue.destinationViewController;
  else if ([segue.identifier isEqualToString: @"secondChildID")
    self.secondChildVC =   (SecondChildVC *) segue.destinationViewController;
  else if ([segue.identifier isEqualToString: @"thirdChildID")
    self.thirdChildVC =   (ThirdChildVC *) segue.destinationViewController;
}

Then define a protocol that you use to communicate with your child view controllers to tell them that they are being shown:

@protocol ChildVCProtocol

- (void) getReadyToShowView;
//whatever other methods
@end

Then make each of your child VCs conform to that protocol. Put your code to get ready to be shown in the getReadyToShowView method. Call that method every time you make a child VC visible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM