UIView subclass with its own XIB

ThomasM picture ThomasM · Mar 9, 2011 · Viewed 22.3k times · Source

I created a custom UIView subclass, and would prefer to not layout the UI in code in the UIView subclass. I'd like to use a xib for that. So what I did is the following.

I created a class "ShareView" which subclasses UIView. I created a XIB file with its file's owner set to "ShareView". Then I link some outlets I declared in my "ShareView.h".

Next I have a ViewController, MainViewController, which adds the ShareView as a subview. whith this code:

NSArray *arr = [[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:nil options:nil];
UIView *fv = [[arr objectAtIndex:0] retain];
fv.frame = CGRectMake(0, 0, 320, 407);
[self.view addSubview:fv];

But now I get NSUnknownKeyException errors on the outlets I declared in my ShareView.

The reason I did all this is because I want a UIView, with its own logic in a seperate XIB file. I read in several places that ViewControllers are only used to manage a full screen, i.e. not parts of a screen... So what am I doing wrong? I want my logic for ShareView in a seperate class, so my MainController class doesn't get bloated with logic from ShareView (which I think is an aption to solve this problem?)

Answer

Yang Meyer picture Yang Meyer · Jul 19, 2011

ThomasM,

We had similar ideas about encapsulating behavior inside a custom view (say, a slider with companion labels for min/max/current values, with value-changed events also handled by the control internally).

In our current best-practice, we would design the ShareView in Interface Builder (ShareView.xib), as described by Eimantas in his answer. We then embed the ShareView to the view hierarchy in MainViewController.xib.

I wrote up how we embed custom-view Nibs inside other Nibs in our iOS developer blog. The crux is overriding -awakeAfterUsingCoder: in your custom view, replacing the object loaded from MainViewController.xib with the one loaded from the "embedded" Nib (ShareView.xib).

Something along these lines:

// ShareView.m
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
    BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);
    if (theThingThatGotLoadedWasJustAPlaceholder) {
        // load the embedded view from its Nib
        ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0];

        // pass properties through
        theRealThing.frame = self.frame;
        theRealThing.autoresizingMask = self.autoresizingMask;

        [self release];
        self = [theRealThing retain];
    }
    return self;
}