6

I have a UIView subclass that I'd like to instantiate from a NIB file. I've been using this method to do it:

UINib *nib = [UINib nibWithNibName:@"SSNodeToolsView" bundle:[NSBundle mainBundle]];
self = [[[nib instantiateWithOwner:self options:nil] objectAtIndex:0] retain];

...and indeed this creates the object correctly and links everything up. However, the object instantly loses access to its delegate. The delegate is declared in the usual way:

id delegate
@property (nonatomic, assign) id delegate
@synthesize delegate

...and so on. However when the object loads from its nib the delegate instantly becomes null and ignores any attempt to assign values to it. Oddly this doesn't cause a crash. Can anyone tell me why this is? I'd prefer to use the nib-loading method since these elements of my app are likely to be redesigned quite frequently and it would be easiest to do that using a nib.

-Ash

Ash
  • 9,064
  • 3
  • 48
  • 59

1 Answers1

7

Edit: moving the key point up to the top. You cannot simultaneously create an object and set it to file's owner all in the same nib. The File's Owner needs to be an already existing and initialized object with nil outlet ivars in order for the nib loading to hook up to the owner's outlets properly.

Is the delegate object also in the nib? If nothing retains it (your property is 'assign') it may just go away at the next autorelease pool drain. Is the delegate an outlet in your nib? "nil" values in outlets after loading a nib is usually a sign that the outlets are not connected correctly. Also, it appears that you're instantiating the nib with an owner object (self) that is not yet initialized? If so, your outlets may not be connected correctly because outlets are only connected if the corresponding value in the owner object is nil. You should have a fully initialized owner object before using it as an owner in loading a nib.

Here's the "load this object from nib" initialization code I use in one of my projects:

- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self) 
    {
        // Initialization code.
        //
        [[NSBundle mainBundle] loadNibNamed:@"ScoreView" owner:self options:nil];
        [self addSubview:self.view];
    }
    return self;
}

- (void) awakeFromNib
{
    [super awakeFromNib];

    [[NSBundle mainBundle] loadNibNamed:@"ScoreView" owner:self options:nil];
    [self addSubview:self.view];
}

Using nibs has a built-in level of indirection that you can't avoid. You can't create an object and simultaneously use it as File's Owner -- the owner must already exist and be initialized (with nil outlets) at nib loading time.

Using the above code I can create my object either from code or place it directly into other nib files -- a reusable UIView component.

Bogatyr
  • 19,255
  • 7
  • 59
  • 72
  • Aha, thankyou! That's why. I'm changing to a more solid method of simply slaving the custom view to the view controller and sending IBActions directly from its buttons. That was I can still use a NIB – Ash Mar 01 '11 at 18:15
  • Yes, that works, but you don't have to. I use the above code in my custom UIView subclasses which are not slaved to a view controller. It's a fairly fine line -- there are definitely some benefits to the viewcontroller approach, but it feels more "heavy weight." And some of the benefits (viewWill/Did/Appear/Disapper) you don't get unless you manually forward these calls yourself through to the sub-viewcontrollers. – Bogatyr Mar 01 '11 at 18:51
  • +1 Great answer!! Especially the "key point" stuff at the top. Obvious once you think about it, but like many others I'm sure, I didn't think about it till I saw your answer. Thanks for saving me time!! – jpswain Aug 07 '11 at 18:06
  • Thanks. Yes the inherent level of indirection in nib files is very tricky at first. – Bogatyr Aug 08 '11 at 13:49
  • If, as the original question stated, the above code is used in a UIView subclass it won't work. There is no self.view property for the line `[self addSubview:self.view];` to work. – Diziet May 09 '12 at 09:28
  • @Diziet naturally specifics must be adjusted to fit the circumstances. My code fragment was not meant as a direct drop-in, but rather an example. The self.view in my code is an outlet defined in the .xib file. The main point of this question and my answer is the point that there is a level of indirection inherent in .xib files. To reiterate: you can't simultaneously create an object and use it as File's Owner loaded from a .xib file. For a more complete treatment of this see my answer here: http://stackoverflow.com/questions/5056219/ – Bogatyr May 09 '12 at 13:11
  • Indeed yes, your code pointed the way and the key point is this extra level of abstraction. I ended up with 'owner:nil' and assigning directly to self (and therefore forgoing a call to super at the top). It's a hack I feel but it works. – Diziet May 09 '12 at 13:31