34

Being new to Cocoa, I'm having a few issues with Interface Builder, UIViewController and friends.

I have a UIViewController subclass with a UIView defined in a xib, and with the controller's view outlet connected to the view. The xib's "file's owner" is set as myViewcontroller subclass.

In this one instance, the following code to load the controller/view (from the main view controller) doesn't work as expected:

if ( self.myViewController == nil )
{
    self.myViewController = [[MyViewController alloc]
        initWithNibName:@"MyViewController" bundle:nil];
}

[self.navigationController 
    pushViewController:self.myViewController animated:YES];

In MyViewController's methods, I have placed breakpoints and log messages to see what is going on:

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        NSLog(@"initWithNibName\n");
    }

    return self;
}

-(void)viewDidLoad {

    [super viewDidLoad];
    NSLog(@"viewDidLoad\n");
}

Expected result

Both -initWithNibName and -viewDidLoad methods are called, and myViewController's view is displayed.

Observed result

Only -initWithNibName is called, the view is not displayed.

Have I missed something? Can anyone recommend anything to check? (Particularly in the wondrously opaque Interface Builder tool).

mfaani
  • 33,269
  • 19
  • 164
  • 293
Justicle
  • 14,761
  • 17
  • 70
  • 94

11 Answers11

36

RE: SOLUTION FOUND!!!!!

Indeed that seems to be a working solution, however the real trick is not in setting the view.hidden property to NO, what makes the view load from the nib file is the calling of the UIViewController's view method, the view only actually gets loaded from the nib when the view method is called for the first time.

In that sense, a simple [viewController view] message would force the view to load from the nib file.

Ravi Gautam
  • 960
  • 2
  • 9
  • 20
Jader Feijo
  • 1,209
  • 1
  • 10
  • 11
15

Ok, I have a partial answer - maybe the gurus can explain some more. The problem is:

[self.navigationController pushViewController:myViewController animated:YES];

Looking more closely, in this case self.navigationController is nil - so the push message is going no-where.

Instead, if I send:

[self.view addSubview:self.myViewController.view];

Then the view appears and -viewDidLoad is called.

I'm not entirely sure why self.navigationController is not set in this instance - the only thing I can think of is that self is a subclass of UIViewController rather than UITableViewController (where the pushViewController code came from).

Also, silently allowing messages to go to nil seems like a bad idea, although these answers say otherwise. See also my question here.

Final edit:

Answers in comments below, I've realised the display function that I was actually after (given myViewController is modal) is:

[self presentModalViewController:myViewController animated:YES];

Thanks everyone for their helpful responses.

Community
  • 1
  • 1
Justicle
  • 14,761
  • 17
  • 70
  • 94
  • 4
    Don't badmouth silent-message-to-nil, you'll use it all the time when you get used to it :-) It can certainly be mysterious when you first start out. As for why navigationController is nil: is it being initialized as the root view controller of a UINavigationController? If not, you can't use pushViewController until you do that! – Adam Ernst May 27 '09 at 20:45
  • 1
    I think Adam has it exactly here, make sure your view is properly placed into a navigation stack before trying to get its navigationController. – Andrew Pouliot May 28 '09 at 02:04
10

SOLUTION FOUND!!!!!

Even something as innocuous as this makes the viewDidLoad method call happen.

Insert this right after alloc initWithNibName

viewController.view.hidden = NO; //calls viewDidLoad
Ravi Gautam
  • 960
  • 2
  • 9
  • 20
  • 5
    that's because every time you use the view-property the getter and/or setter will trigger loading of the view. even just typing 'self.view;' would be sufficient enoguh – hfossli Jan 19 '11 at 14:10
  • But doing that is probably not recommended, since it short circuits the whole lazy loading scheme. – tomwhipple Apr 07 '11 at 00:31
5

Chances are that you might not have linked the supposed ViewController in main.storyboard from the Identity Inspector to the custom class you created. You might be able to navigate to that controller from other view controllers via segues but any of viewDidLoad(), viewWillAppear() etc. won't be executed.

Dovydas Šopa
  • 2,282
  • 8
  • 26
  • 34
Yash Tamakuwala
  • 1,789
  • 1
  • 24
  • 33
5

make sure that the view outlet in File's Owner (your viewController subclass) is connected to the actual view (i.e. the 480X320 canvas you see on your screen that you use to build your UI)

zpesk
  • 4,343
  • 7
  • 39
  • 61
4

Another reason, somewhat obvious in retrospect: if viewController.view is set in code, then the viewDidLoad event will not trigger.

nmr
  • 16,625
  • 10
  • 53
  • 67
4

Simply use

- (void)viewDidAppear:(BOOL)animated{ 
        [super viewDidAppear:animated];
        //Your Code here
  }

instead of the viewDidLoad method.

Lolloz89
  • 2,809
  • 2
  • 26
  • 41
3

It looks like a capitalization problem to me. You're referencing the class MyViewController instead of the property myViewController in the call to pushViewController.

slf
  • 22,595
  • 11
  • 77
  • 101
  • My bad that was just a typo while moving code to stackoverflow - I'm fixing the question now. Thanks. – Justicle May 27 '09 at 02:33
2

Check your run log for errors. Almost certainly, the NIB is not loading, and there should be an error to that effect. The most likely cause for that is failure to put it in the bundle. Look in your "Copy Resources" build phase and make sure that the XIB is actually being copied. Build for the simulator, and go down into the build directory and make sure that the NIB is in the .app bundle.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • All good points, CompileXIB is being called, and MyViewController is both in the Copy Resources phase and appearing in the app package. Checked this a few times with a clean build. Also I modified the -initWithNibName NSLog to write 'self', with the result: "initWithNibName self=", so it looks like its being loaded. – Justicle May 27 '09 at 03:24
  • 1
    initWithNibName:bundle: doesn't load the NIB. The first call to -view loads the NIB (so checking self doesn't prove anything). Check that self.naviagationController isn't nil. If it were, then you'd never actually load the NIB. Also, you're likely leaking in your assignment to self.myViewController (assuming it's a "retain" property, which it should be). You need to autorelease it. Kudos for using accessors there, though. In Cocoa, when "nothing happens" something is nil. The most common case is you didn't wire it in IB. – Rob Napier May 27 '09 at 03:51
  • Ah thanks Rob, didn't see your comment there - the problem was in fact that self.navigationController was nil. Not sure why (see my answer below). – Justicle May 27 '09 at 06:10
0

Apart from other answers here,

It often happens when the identifier with which you instantiate your ViewController from the storyboard is incorrect. For e.g.

[[self getStoryboard] instantiateViewControllerWithIdentifier:MyVC];

If MyVC is the identifier of some other ViewController, this might happen.

OP is using nib instead of storyboard here. But the answer applies.

iphondroid
  • 498
  • 7
  • 19
0

The page has been presented but not visible in Debug view hierarchy & in device(simulator also), issue happens based on and i found the fix:

func viewWillLayoutSubviews{
     if day == true{
            self.view.backgroundColor = .clear
     }else{
            self.view.backgroundColor = .blue
     }
}

Don't try to implement the self.view (viewcontrollers view) in function of layoutsubviews. So better use self.view in viewwillappear or viewdidload. This issue happens starts from v-14 devices.

Hope it works for you too.

Dilip M
  • 1
  • 3