Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading UIView from a nib file without guesswork

Ok, here's another question.

I am creating a UIView called ProgressView that is a semi-transparent view with an activity indicator and a progress bar.

I want to be able to use this view throughout different view controllers in my app, when required.

I know of 3 different ways of doing this (but I am only interested in one):

1) Create the entire view programatically, instantiate and configure as required. No worries I get that one.

2) Create the UIView in interface builder, add the required objects and load it using a method like the below. Problem with this is that we are basically guessing that the view is the objectAtIndex:0 because nowhere in the documentation I found a reference to the order of the elements returned from the [[NSBundle mainBundle] loadNibName: function.

NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"yournib" 
                                                 owner:self 
                                               options:nil];
UIView *myView = [nibContents objectAtIndex:0];
myView.frame = CGRectMake(0,0,300,400); //or whatever coordinates you need
[scrollview addSubview:myView];

3) Subclass UIViewController and let it manage the view as per normal. In this case I would never be actually pushing the view controller onto the stack, but only its main view:

ProgressViewController *vc = [[ProgressViewController alloc] initWithNibName:@"ProgressView" bundle:nil];
[vc.view setCenter:CGPointMake(self.view.center.x, self.view.center.y)];
[self.view addSubview:vc.view];
[vc release];

As far as I can tell, #3 is the the correct way of doing this (apart from programatically) but I am not entirely sure if it is safe to release the ProgressView's view controller whilst another controller's view is retaining its main view (gut feel says it is going to leak?)?

What do I do in terms of memory management in this case, where and when should I release the ProgressView's view controller?

Thanks in advance for your thoughts.

Cheers,

Rog

like image 571
Rog Avatar asked Mar 24 '11 02:03

Rog


People also ask

What is a UINib?

A UINib object caches the contents of a nib file in memory, ready for unarchiving and instantiation. When your app needs to instantiate the contents of the nib file, it can do so without having to load the data from the nib file first, which improves performance.

What is file's owner in Xcode?

The File owner is the object that loads the nib, i.e. that object which receives the message loadNibNamed: or initWithNibName: . If you want to access any objects in the nib after loading it, you can set an outlet in the file owner.


1 Answers

I think that your solution #3 adds unnecessary complexity by introducing a UIViewController instance just as a container for your ProgressView so that you can setup nib bindings. While I do think that it is nice to be able to work with an IBOutlet bound property rather than iterating through the nib contents you can do so without introducing a UIViewController whose behavior you neither need nor want. This should avoid your confusion around how and when to release the view controller and what, if any, side effects it might have on the responder chain or other behaviors of the loaded view.

Instead please reconsider using NSBundle and taking advantage of the power of that owner argument.

@interface ProgressViewContainer : NSObject {

}

@property (nonatomic, retain) IBOutlet ProgressView *progressView;

@end

@implementation ProgressViewContainer

@synthesize progressView = progressView;

- (void) dealloc {
    [progressView release];
    [super dealloc];
}

@end

@interface ProgressView : UIView {

}

+ (ProgressView *) newProgressView;

@end

@implementation ProgressView

+ (ProgressView *) newProgressView {
    ProgressViewContainer *container = [[ProgressViewContainer alloc] init];
    [[NSBundle mainBundle] loadNibNamed:@"ProgressView" owner:container options:nil];

    ProgressView *progressView = [container.progressView retain];
    [container release];
    return progressView;
}

@end

Create a nib named "ProgressView" containing a ProgressView and set it's File's Owner class to ProgressViewContainer. Now you can create ProgressViews loaded from your nib.

ProgressView *progressView = [ProgressView newProgressView];
[scrollView addSubview:progressView];
[progressView release];

If you have multiple configurations of your progress view then maybe you'll want to implement a -initWithNibNamed: method on ProgressView instead of +newProgressView so you can specify which nib to use to create each ProgressView instance.

like image 80
Jonah Avatar answered Nov 11 '22 02:11

Jonah