37

I am using storyboard instantiateViewControllerWithIdentifier: and I'm noticing that all the IBOutlets I have wired up are still nil. However, the IBActions I have wired up work. The view and controller are linked (i.e controller.view is not nil), and if I show the view it displays what I am expecting.

What am I missing?

Here's my setup:

  1. I've got a View Controller defined in my storyboard. I have given it an identifier which is the same identifier I use when invoking instantiateViewControllerWithIdentifier:
  2. I've set up the view's owner by clicking on the View Controller (just under First Responder) and under the Identity Inspector set the Custom Class to be the same name as the class I want to wire the view to.
  3. Then I open up Assistant editor, and control dragged the UI elements to create the IBOutlets and IBActions.
BlueFish
  • 5,045
  • 3
  • 26
  • 35
  • I've tried breaking the view out into its own nib and I'm seeing the same problems. initWithNibName:bundle: also doesn't seem to set IBOutlets even though they are defined through ctrl-click-drag in IB. – BlueFish Sep 21 '12 at 02:00
  • As a work around, I'm using [[NSBundle mainBundle] loadNibNamed:@"myNib" owner:controller options:nil]. This does load the nib and assigns the IBOutlets to the values in the controller. But this just seems wrong when there is a method on the controller that looks like it's doing the same thing. – BlueFish Sep 21 '12 at 02:12
  • I am a bit confused with your set up, why are you setting up a view's owner? your ib outlets are placed in a viewcontroller on the story board right? when you instantiate this viewcontroller the outlets should work if you set the type of this VC to match your custom header and implementation. – Pochi Sep 21 '12 at 02:23
  • Correction: I did not set the view's owner, but the view controller class type to match the custom class type. – BlueFish Sep 21 '12 at 04:27
  • If you control click on the iconic representation of your view controller in the storyboard to bring up the connections HUD, do you see all of your outlets connected? – Jon Hess Sep 21 '12 at 04:39
  • Yes. All outlets are connected. Right click also shows that all the items are connected. – BlueFish Sep 21 '12 at 05:57

6 Answers6

71

The view seems to be initialized properly only after it is accessed first. The problem goes away when calling

[self presentViewController:vc animated:NO completion:nil];

or more simply

[vc view];
Stefan Henze
  • 2,711
  • 23
  • 22
  • 6
    This is really odd. I mean, do you know if this is proper behavior? This had been driving me nuts for two days now until I found your answer. – jere Aug 21 '13 at 14:57
  • I'm not sure if this is proper, but it's understandable. Why setting up a view and everything just because a view controller was created. One could assume that you want the view when you have the view controller but who knows what optimization led to this behavior. It should be documented somewhere though. – Stefan Henze Aug 21 '13 at 17:25
  • 1
    It works,thanks! I agree, it should be covered by the documentation – Claus Jun 23 '14 at 15:49
4

[Use me as bad example]

Maybe not a good idea after all, it works but it violates the regular loading making the app unstable ^_^.
I'll leave the answer here in case someone else want to know if you do it.


I was having the same issue, however the custom component I designed isn't loaded via presentViewController (load in overlay to the previous view)

You can simply call

myViewController.loadView() //Swift

[myViewController loadView] //Obj-C

Cesar
  • 4,418
  • 2
  • 31
  • 37
  • 8
    From the docs: **`You should never call this method directly.`**. First I just blindly copied this code and my view was loaded but then I ran into other problems which I couldn't explain. After removing this code, everything worked. I highly recommend not to call this method. – hashier Sep 26 '16 at 14:58
  • 1
    I recommend not following the above comment – Warrick Apr 13 '17 at 02:55
  • 1
    Wrong answer: This will suppress the calling of viewDidLoad()! If you - by all means - must force the loading of a view - do it using loadViewIfNeeded(). – iDoc Aug 21 '19 at 18:54
0

Outlets are established with Key Value Coding, you could put this into your view controller subclass:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath {
    [super setValue:value forKeyPath:keyPath];
}

- (void)setValue:(id)value forKey:(NSString *)key {
    [super setValue:value forKeyPath:key];
}

And put a breakpoint on the two calls to super.

In the debugger, I'd try:

  1. Comparing 'self' to the view controller you think you're applying the outlets to. Perhaps your storyboard has a second view controller you're not expecting. When you said you 'set up an owner' I thought that was strange terminology. Did you drag out one of the cube objects to represent your view controller in addition to the representation that's already there?

  2. Compare [self valueForKey:key] or [self valueForKeyPath:keyPath] or just [self outletName] with the passed in value after the call through to super. Maybe you have setter that isn't doing what you intend. I've seen one mistake like this come up a couple of times where people have an outlet for an object with a name like "firstName" and then an action with the selector "setFirstName:". That confuses key value coding, and you end up with setFirstName: getting called during nib loading with intent of establishing the outlet, but the setter for the outlet is implemented as an action method, so the outlet is never set.

Jon Hess
  • 14,237
  • 1
  • 47
  • 51
0

Setting the frame for the instantiated ViewController seems to set the IBOutlets.

ex:

DemoViewController *demoVC = [[self storyboard] instantiateViewControllerWithIdentifier:@"demoVC"];
demoVC.frame = self.view.frame; //or CGRectMake(0, 0, 1015, 604);

This would initialize the IBOutlets of DemoViewController like Label, TextField, Button, Table etc,.

Hope this helps.

Sushma Satish
  • 2,363
  • 2
  • 20
  • 23
  • 3
    It's accessing the `view` what does the magic, not setting the `frame`. It applies for outlets that are inside of the view hierarchy (not, say custom `NSObject` ones). – Rudolf Adamkovič Oct 11 '13 at 12:48
0

If the object you're linking to is a top-level entity in the scene, like an ArrayController, the reference needs to be strong (not weak!) since it's not retained by the view hierarchy. A weak reference can cause a problem like what you describe.

Look here for more information: Should IBOutlets be strong or weak under ARC?

Community
  • 1
  • 1
zgchurch
  • 2,302
  • 1
  • 14
  • 7
0

People gave solution of how to get the view loaded (some of which you should never use (loadView()) so here is a way to check whether your view is already loaded

I ran into the problem because I had a property with a didSet observer to update the UI which obviously doesn't work if the outlets are not yet set, so here is some example code how to work around it:

var name: String? {
    didSet {
        updateNameLabel()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    updateNameLabel()
}

func updateRoomLabel() {
    guard isViewLoaded() else {
        return
    }
    [...]
}

So now when you display the outlets get updated but also every time you update the property

hashier
  • 4,670
  • 1
  • 28
  • 41