1

I have a custom container view controller that I instantiate from a storyboard and that has a bunch of methods that modify the content of subviews that I've set outlets to from the storyboard.

There are a bunch of ways that I might instantiate this ViewController, and at present I have to make sure that, however I instantiate it, I either display it, explicitly call loadView, or access its .view property before I start doing anything that uses its outlets (since they're all null pointers until loadView is called).

Ideally, I'd like to put a call to loadView or .view in a single initialiser method of my ViewController to get around this problem, rather than having to put the call to .view in a bunch of different places where I initialise the ViewController from.

Does the UIViewController class have a designated initialiser? If not, what methods do I need to modify with my custom initialisation logic to ensure that it will be called on initialisation of my ViewController no matter what?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • The UIViewController documentation explicitly states that `loadView` should never be called directly. – Martin R Jun 28 '13 at 13:46
  • @MartinR Yes, and Xcode's warnings also state unambiguously that property getters shouldn't be used for side effects. Nonetheless, if you want to do subview manipulation via outlets before you've displayed the ViewController and caused `loadView` to be called, you have no choice but to use one of these two forbidden approaches - unless I'm missing an alternative, in which case please provide it here: http://stackoverflow.com/a/1935566/1709587 – Mark Amery Jun 28 '13 at 13:50

2 Answers2

3

awakeFromNib seems to be a suitable place for your purpose. From the documentation:

During the instantiation process, each object in the archive is unarchived and then initialized with the method befitting its type. Objects that conform to the NSCoding protocol (including all subclasses of UIView and UIViewController) are initialized using their initWithCoder: method.
...
After all objects have been instantiated and initialized, the nib-loading code reestablishes the outlet and action connections for all of those objects. It then calls the awakeFromNib method of the objects.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    It's worth noting here that the guarantee made in the quoted documentation that outlet variables are set by the time `awakeFromNib` is called is violated in the case where the `ViewController` is loaded from a storyboard rather than a `.nib`. See http://stackoverflow.com/questions/17400547/awakefromnib-outlets-and-storyboards-is-the-documentation-wrong and http://stackoverflow.com/questions/2723042/accessing-view-in-awakefromnib – Mark Amery Jul 01 '13 at 21:24
-1

You can override these to cover the init cases:

- (id) initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if(self)
    {
        [self customInit];
    }
    return self;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        [self customInit];
    }
    return self;
}

- (id)init
    {
        self = [super init];
        if (self) {
            [self customInit];
        }
        return self;
    }

- (void) customInit
{
    //custom init code
}

However this is not good practice and you should do your subview manipulation in viewDidLoad.

foggzilla
  • 1,705
  • 11
  • 15
  • Thanks. I actually just figured this out myself - I probably ought to have done a little more research before asking - but: 1) I'm not intending to do any subview manipulation in the initialiser; as I explained, I just want to call `[self loadView]` so that the subviews get loaded and the outlets are non-null. 2) Calling `[self loadView]` in `initWithCoder` throws an NSInternalInconsistencyException when a ViewController is being instantiated through `[Storyboard instantiateViewControllerWithIdentifier]`, it seems. -1 since this doesn't handle the use case I asked for. – Mark Amery Jun 28 '13 at 13:48
  • @MarkAmery: Is a downvote really necessary here? You asked about the designated initializer and you got the answer. You cannot expect that foggzilla creates a test project to check if `[self loadView]` works in `initWithCoder` or not. - Actually I hesitate now to give an alternative answer, because you would downvote it if it does not work in your use case ... – Martin R Jun 28 '13 at 13:56
  • @MartinR I provided the use case in the question. I certainly don't think foggzilla could've predicted the problem I ran into trying to use `initWithCoder` without testing, and I might've given the exact same answer he did if I were in his position - I'm grateful for his help. Nonetheless, it's not a correct answer to the question as currently stated, so I've got to -1 it. – Mark Amery Jun 28 '13 at 14:02
  • 1
    thank you @MartinR, you never know when you are about to step on eggshells in answering – foggzilla Jun 28 '13 at 17:07
  • @foggzilla: You have answered his main question *"Does the UIViewController class have a designated initialiser?"*, so in my opinion a *"Thank you, your answer is correct but it turned out that is does not help in my case"* would have been sufficient. - But there is nothing one can do about it ... – Martin R Jun 28 '13 at 17:20