Rob Booth
Rob Booth

Reputation: 1782

Proper way to init a UIView with .xib and backing class

I have some custom created UIViews that use .xib files for layout, and backing classes for extra setup. I create these classes using alloc/init and calling loadNibNamed in my custom init method but in doing so was causing memory leak. Someone pointed out that the alloc portion actually created a self object that was leaking so I adjusted my init method to this one:

- (id)init 
{
    [self autorelease];
    self = [[[[NSBundle mainBundle] loadNibNamed:@"AssignmentView" owner:nil options:nil] lastObject] retain];
    [self setupBranding];

    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
    [self addGestureRecognizer:tapRecognizer];
    [tapRecognizer release];

    return self;
}

However, now when I run analyze code I get this warning "Returning 'self' while it is not set to the result of '[(super or self) init...]'". So my question is what is the correct way for doing custom UIViews with a backing class?

Since it was asked I'd used this above code like this:

AssignmentView * assignmentView = [[AssignmentView alloc] init];
[self.view addSubview:assignmentView];

Upvotes: 9

Views: 15839

Answers (2)

Dave Batton
Dave Batton

Reputation: 8845

Get the view from -[NSBundle loadNibNamed:owner:options:], then set its frame to match your view's bounds. Generally you'll also want to make the view in the nib resize when its parent view is resized.

We've got a UIView category with these additions:

- (UIView *)viewFromNib
{
    Class class = [self class];
    NSString *nibName = NSStringFromClass(class);
    NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
    UIView *view = [nibViews objectAtIndex:0];
    return view;
}


- (void)addSubviewFromNib
{
    UIView *view = [self viewFromNib];
    view.frame = self.bounds;
    [self addSubview:view];
}

Then our -initWithFrame: method looks like this:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubviewFromNib];
    }
    return self;
}

Just make sure the nib has the same name as the class for this to work.

Upvotes: 19

Jeffery Thomas
Jeffery Thomas

Reputation: 42588

Would you consider using the convenience constructor style?

+ (AssignmentView *)assignmentView
{
    AssignmentView *result = [[[NSBundle mainBundle] loadNibNamed:@"AssignmentView" owner:nil options:nil] lastObject];
    [result setupBranding];

    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
    [result addGestureRecognizer:tapRecognizer];
    [tapRecognizer release];

    return result;
}

It gives you the flexibility you seem to need when construct your view, but doesn't cause a memory leak or warnings.

Upvotes: 7

Related Questions