26

I have a UIViewController called RootViewController and A UIViewController called NewsViewController. I want to show NewsViewController inside a UIView (the UIView is just a part of the screen) I created inside RootViewController. I'm using StoryBoard and my app needs to support iOS 5 (so I can't use embedded segues aka Containers from the IB)

RootViewController code:

- (void)viewDidLoad
{
    [super viewDidLoad];
    NewsViewController *news = [[NewsViewController alloc]init];
    news.view.frame = self.newsSection.bounds;
    [self.newsSection addSubview:news.view];
    [self addChildViewController:news];
    // Do any additional setup after loading the view.
}

I also connected both UIViewControllers with a segue. The UIView newsSection will stay empty. What Am I doing wrong?

Edit:

This works for me, is that the right approach?

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:@"NewsViewControllerID"];
    news.view.frame = self.newsSection.bounds;
    [self.newsSection addSubview:news.view];
    [self addChildViewController:news];
    [news didMoveToParentViewController:self];
}
Paul Peelen
  • 10,073
  • 15
  • 85
  • 168
Segev
  • 19,035
  • 12
  • 80
  • 152
  • **Explained fully** with modern syntax, and how to remove, in the last section of this answer: https://stackoverflow.com/a/23403979/294884 – Fattie Sep 24 '19 at 14:58

6 Answers6

44
- (void)viewDidLoad
{
    [super viewDidLoad];
    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:@"NewsViewControllerID"];
    news.view.frame = self.newsSection.bounds;
    [self.newsSection addSubview:news.view];
    [self addChildViewController:news];
    [news didMoveToParentViewController:self];
}

Swift

override func viewDidLoad() {
    super.viewDidLoad()
    let news = self.storyboard?.instantiateViewControllerWithIdentifier("NewsViewControllerID") as! NewsViewController
    news.view.frame = newsSection.bounds
    newsSection.addSubview(news.view)
    addChildViewController(news)
    news.didMoveToParentViewController(self)
}

Swift >= 3:

override func viewDidLoad() {
    super.viewDidLoad()
    let news = self.storyboard?.instantiateViewController(withIdentifier: "NewsViewControllerID") as! NewsViewController
    news.view.frame = newsSection.bounds
    newsSection.addSubview(news.view)
    addChildViewController(news)
    news.didMove(toParentViewController: self)
}
Taiwosam
  • 469
  • 7
  • 13
Segev
  • 19,035
  • 12
  • 80
  • 152
  • 1
    Use self.storyboard in a view controller instead of reloading it. – Peter DeWeese Oct 08 '14 at 14:33
  • I gave this a go not really expecting it to do exactly as I wanted... but it did! I had an infinite loop at first because I was loading the view controller in the class itself. Once I moved the code to `initWithNibName` everything worked great. That fixed it because `[storyboard instantiate..]` calls `initWithCoder`, thereby avoiding the recursive infinite loop. – Matt Dec 15 '14 at 20:07
10

Create child, when creating root VC:

-(void)awakeFromNib
{
  [super awakeFromNib];
  // Create news vc from storyboard
  UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"YourStoryboard" bundle:nil];
  NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:@"News VC ID, you should set it in IB"];
  // Add it as a child to root
  [self addChildViewController:news];
  [news didMoveToParentViewController:self];
  // Save it for future use
  self.news = news;
}

Add child's view when root's view is created:

-(void)viewDidLoad
{
  [super viewDidLoad];
  self.news.view.frame = self.newsSection.bounds;
  [self.newsSection addSubview:self.news.view];
}
Alexey Kozhevnikov
  • 4,249
  • 1
  • 21
  • 29
  • Your solution worked. Thanks. I'm wondering, why should I use awakeFromNib instead of my edited question (I added a temp solution there) – Segev Jul 25 '13 at 09:52
  • On iOS < 6 view can be loaded/unloaded more than once, so every time viewDidLoad is called instance of NewsViewController will be created and added as a child. Of course you can handle viewDidUnload and remove it there, but creating child when creating parent is better in my opinion. – Alexey Kozhevnikov Jul 25 '13 at 11:41
1

Try this:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
NewsViewController *rootViewController = 
 [storyboard instantiateViewControllerWithIdentifier:@"NewsViewControllerID"];
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
Lithu T.V
  • 19,955
  • 12
  • 56
  • 101
  • Won't that show the ViewController over the entire screen? I want to show it only on part of the root view controller screen – Segev Jul 25 '13 at 08:51
  • 1
    Why dont you just create a UIView class instead of UIViewcontroller? It can jus be added with `addsubview:` method – Lithu T.V Jul 25 '13 at 09:19
1

Here is the approach the way it worked for me.

I have 3 buttons at the top Purchase,Sale, Rental. now at tapping at any particular button i am loading corresponding UIViewController and i am using storyboard.

I specified Storyboard Id for every UIViewController.

I added two UIView to my Main ViewController which have these 3 buttons(Purchase, Sale ,Rental).

one view i am using for the above three buttons and other i am using for loading a UIViewController.

Now at tapping a specific button i am and in tapping a specific button i am doing following code.

- (IBAction)sellingClicked:(id)sender
{        
    UIStoryboard *storyboard=[UIStoryboard storyboardWithName:@"Main" bundle:nil];

    TestViewController *tvc=[storyboard instantiateViewControllerWithIdentifier:@"test"];

    [self.viewContainer addSubview:tvc.view];
    [self addChildViewController:tvc];       
}

and its working perfectly for me further you can add scrolling View i you want to scroll there.

Akshit Zaveri
  • 4,166
  • 6
  • 30
  • 59
Husrat Mehmood
  • 2,270
  • 1
  • 20
  • 22
  • 1
    Use `self.storyboard` instead of loading the entire storyboard. That will suck up a lot of memory and CPU. – Andy Mar 02 '15 at 21:42
1
        otherController!.view.frame = container.bounds;
        self.addChildViewController(otherController!);
        container.addSubview(otherController!.view);
        container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: .allZeros, metrics: nil, views: ["view": otherController!.view]))
        container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: .allZeros, metrics: nil, views: ["view": otherController!.view]))
        otherController!.didMoveToParentViewController(self);

Don't forget to specify constraint especially if you want to perform animations

Vassily
  • 899
  • 2
  • 8
  • 19
0

You should move your code to - (void) viewDidAppear: (BOOL) animated like this

- (void) viewDidAppear: (BOOL) animated
{
    [super viewDidAppear: animated];

    NewsViewController *news = [[NewsViewController alloc]init];
    news.view.frame = self.newsSection.bounds;
    [self.newsSection addSubview:news.view];
    [self addChildViewController:news];
    // Do any additional setup after loading the view.
}

because frame and bounds in viewDidLoad is {0,0} {0,0}

stosha
  • 2,108
  • 2
  • 27
  • 29
  • can you insert NSLog(@"%@", NSStringFromCGRect(self.newsSection.bounds)); after [super viewDidAppear: ... ? – stosha Jul 25 '13 at 09:51
  • 2013-07-25 12:54:04.470 containerTests[6683:11303] {{0, 0}, {320, 146}} – Segev Jul 25 '13 at 09:54
  • it works. your "news" view controller hasn't any content. you can change background color for news.view and will see. – stosha Jul 25 '13 at 09:56
  • It got a different color. It doesn't work when I use NewsViewController *news = [[NewsViewController alloc]init]; only with instantiateViewControllerWithIdentifier – Segev Jul 25 '13 at 09:59