4
votes

I'd like to design a UIView and some sub-views (UIWebView, UIToolbar, some UIBarButtonItems, a progress indicator and so-forth) using the Interface Builder, but I think it's unnecessary to do this traditionally, by using a UIViewController, using presentViewController:animated etc.

So, I created a custom class, with the .h file code as follows:

@interface FileInteractionManager : NSObject {

}

@property (nonatomic, retain) IBOutlet UIView *fileView;
@property (nonatomic, retain) IBOutlet UIWebView *fileWebView;

@property (nonatomic, retain) IBOutlet UIBarButtonItem *printButton;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *optionsButton;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *doneButton;

My .m file is as follows:

@implementation FileInteractionManager

@synthesize fileView, fileWebView, doneButton, optionsButton, printButton;

-(id)init {

    NSArray *array = [[NSBundle mainBundle] loadNibNamed:@"FileInteractionView" owner:self options:nil];

    NSLog(@"Load success!");

    return self;
}

Finally, I create a stand-alone xib file named 'FileInteractionView.xib', change the file's owner to the custom class I created above, and wire up the IBOutlets.

When I call the init method on my class, I can see in the debugger that all my IBOutlet objects are instantiated properly.

My questions are:

  1. Is the loadNibNamed:owner:options: method the right way to load my stand-alone .xib file? I don't like the fact that this method returns an array I have no use for (the top-level object returned matches my variable fileView, but I've already linked them through the Interface Builder).

  2. Is my general approach correct in solving my problem? I carried out the above steps because I wanted a simple UIView object that I could add to my existing UIViewController, rather than present and dismiss a whole new UIViewController.

2

2 Answers

6
votes

I use a little different approach. I create a subclass of UIView (MyCustomView i.e.) then the xib with the UI of the view and change the (main) view class the the one just defined. In the xib then you can link the outlet to the custom view itself (not the file owner). Finally in the class definition I create a function like this:

+ (id) newFromNib
{
    NSArray *nibArray = [[UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil] instantiateWithOwner:nil options:nil];
    return nibArray[0];
}

Just a couple of notes: 1) this's a class method, you can use "self" just for stuff like "NSStringFromClass([self class])" but the real object is the variable returned 2) this example suppose the xib have the same name of the class (via NSStringFromClass([self class]) so I can copy-paste it without changing anything ;) ) and that your view is the first one defined in the xib (the standard). If you store more than a "main" view inside one xib pick the right element.

so where I need MyCustomView I do something like:

MyCustomView* mycv = [MyCustomView newFromNib];

then set frame/center and add to superview... I think this way is pretty usefull if you have a "library" of complex UI elements and want to design them via xib then add when needed.

1
votes

il Malvagio Dottor Prosciutto answer is nice. Here is a possible alternative.

Load nib in NS_DESIGNATED_INITIALIZER and become owner of subview

If we accept the xib to only hold a subview instead of the view itself, then we can load the subview in initWithFrame: and keep an ownership construction in xib.

@interface MyCustomView ()
@property (strong, nonatomic) IBOutlet UIView *subview;
@end

@implementation MyCustomView
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    [self addSubview:self.subview];
    return self;
}
@end