20

I have a UIView that I want to use in several View Controllers so I created the view (.h/.m) and a nib file for it. In Storyboard view is added as subview to view controller but is not loading the layout as defined in nib.

Is it possible to load the nib for a UIView used within a view controller in a Storyboard?

tiltem
  • 4,572
  • 3
  • 21
  • 26

3 Answers3

18

If you ask about seeing your .xib content in Interface Builder when editing Storyboard, it's not possible. But if you need to load it in runtime, it's possible.

  • .h/.m should contain UIView subclass
  • in .xib file you need to set File's owner (Identity Inspector -> Custom Class) with your custom UIView subclass
  • in .h file add @property (strong, nonatomic) IBOutlet UIView* view; and connect your View container from Interface Builder to this field (Ctrl + Click on View and drag connection to above view property in .h
  • connect other subviews to .h
  • in .m file you need to place


- (void)awakeFromNib {
    NSString* nibName = @"Place here your Nib name without extension";
    if ([[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]) {
        [self.view setFrame:[self bounds]];
        [self addSubview:self.view];
    }
}

That's all. Move View to Storyboard scene, change Custom Class to your FooView, import .h in View Controller code and use IBOutlets as you want.

Have happy coding

EDIT

I want to notice that according awakeFromNib documentation we should avoid such code in awakeFromNib.

It is recommended that you maintain a one-to-one correspondence between your File’s Owner objects and their associated nib files. Loading two nib files with the same File’s Owner object causes that object’s awakeFromNib method being called twice, which could cause some data structures to be reinitialized in undesired ways. It is also recommended that you avoid loading other nib files from your awakeFromNib method implementation.

Anton Gaenko
  • 8,664
  • 6
  • 41
  • 38
  • So if I have FooView.h/.m & FooView.xib and have added FooView to a ViewController in Storyboard and created IBOutlet of it in ViewController. What I am not clear on is the "view" property of FooView. Is this needed, or can the FooView that is defined in ViewController of Storyboard be instantiated or awaken in another way? – tiltem Jan 23 '14 at 17:50
  • @tiltem usually you create **View Controller** and .xib in couple. So all binding of top view container (in .xib) and **view** property in VC is made automatically in such case. Here you want to bind UIView to .xib. So you need to do this routine work manually. But it allows you to use Auto Layout in .xib and Storyboard too. It's minimal price for that. Is it clear now? – Anton Gaenko Jan 23 '14 at 18:59
  • 1
    you can find helpful this [link](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html) – Anton Gaenko Jan 23 '14 at 19:03
  • 2
    This is rarely described but extremely valuable piece of explanation. I love it. The same method is mentioned here: http://onedayitwillmake.com/blog/2013/07/ios-creating-reusable-uiviews-with-storyboard/ – algal May 09 '14 at 05:59
  • Hmm.. may be I did not get what you are saying here. Are you suggesting to add the above code (loadNibNamed) in the class that you are loading? Would not this go in infinite loop? – Shirish Kumar May 17 '15 at 00:45
  • @ShirishKumar yeap, in fact it's not calling twice but see my update – Anton Gaenko May 18 '15 at 07:55
10

Name your view ViewInNib, then we have:

  1. ViewInNib.h
  2. ViewInNib.m
  3. ViewInNib.xib

In your storyboard, put a placeholder UIView component in the view controller, remember to set it's class ViewInNib, then in ViewInNib.m:

static BOOL isReplaced = NO;
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
    if (!isReplaced) 
    {
        isReplaced = YES;
        ViewInNib *replaced = [[[NSBundle mainBundle] loadNibNamed:@"ViewInNib" owner:nil options:nil] lastObject];
        replaced.frame = self.frame;
        replaced.translatesAutoresizingMaskIntoConstraints = NO;
        return replaced;
    }
    isReplaced = NO;
    return self;
}

In this method, we replace real view loaded from nib from view in storyboard. Hope it helps.

ThomasW
  • 16,079
  • 4
  • 73
  • 101
sunnyxx
  • 547
  • 6
  • 12
  • This is very intriguing, but I can't get it to work. Should it still necessary to set the File's Owner as in Anton Gaeko's answer? Does this have any disadvantages with respect to that answer's approach? – algal May 09 '14 at 06:21
  • 1
    Now, you may try "pod search XXNibBridge" – sunnyxx Jan 21 '15 at 08:39
  • Why are we setting isReplaced = YES in the if block, if we set isReplaced = NO right after? Doesn't that override the setting in the if block? Is this an error? – Jake T. Jun 10 '16 at 17:23
  • BEcause there's a return in the if statement so setting it to 'no' is never reached. On a side note, that statement is unneeded anyway because the only way it can reach that code is if it's already set to no. – Mark A. Donohoe Sep 22 '16 at 19:07
4

use XXNibBridge:

https://github.com/sunnyxx/XXNibBridge

Super simple to use and works with Swift as well. Full credit goes to @sunnyxx

Robert Wagstaff
  • 2,594
  • 1
  • 24
  • 40