Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom UIView from nib inside another UIViewController's nib - IBOutlets are nil

I'm trying to create a custom UIView which holds references to its own IBOutlets. I then want to put this custom UIView into another nib.

I'm doing some additional logic in the custom UIView's awakeFromNib method. Unfortunately, when I try to access the IBOutlets in awakeFromNib, they are nil.

Here's the setup:

  1. I have a UIView subclass, CustomView.
  2. I have a custom .xib file with three subviews
  3. In the other nib (that belongs to the view controller), I have dragged a UIView onto the view, and then changed the custom class to CustomView.
  4. I tried setting the view in the CustomView nib in IB to a custom class CustomView and connecting the IBOutlets to the view, but they were still nil.
  5. I tried setting file owner to CustomView and connecting the IBOutlets to file's owner, but they were still nil.
  6. I also tried using another IBOutlet UIView *view and then adding that as a subview to self in awakeFromNib but that also didn't do anything.

Here's the code:

// CustomView.h
@interface CustomView : UIView

@property (nonatomic, retain) IBOutlet UITextField *textField;
@property (nonatomic, retain) IBOutlet UIView *subview1;
@property (nonatomic, retain) IBOutlet UIView *subview2;

// CustomView.m
@implementation CustomView
@synthesize textField, subview1, subview2; 

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];

    if (self) {
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [self setup];
}

- (void)setup {
    // Fails because self.textField is nil
    self.textField.text = @"foo";
}
like image 534
Dr. Acula Avatar asked Nov 16 '13 03:11

Dr. Acula


2 Answers

I ended up using the steps in the most recent edit here and they worked beautifully.

You use a plain UIView as the top level view in the xib.

You then set file's owner to the custom subclass (CustomView).

Finally, you add a line:

[self addSubview:[[[UINib nibWithNibName:@"CustomView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0]];

in the if (self != nil) block in both initWithCoder and initWithFrame.

Voila! The IBOutlets are hooked up and ready to go after the call. Really pleased with the solution, but it was very difficult to dig up.

Hope this helps anyone else.


EDIT: I updated the link to one that isn't dead. Since I never spelled out the full code, here is what it looks like after modification:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        UIView *nib = [[[UINib nibWithNibName:@"CustomView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
        [self addSubview:nib];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];

    if (self) {
        UIView *nib = [[[UINib nibWithNibName:@"CustomView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
        [self addSubview:nib];
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [self setup];
}

- (void)setup {
    // Doesn't fail because life is awesome
    self.textField.text = @"foo";
}

This pattern has become so common that I actually created a category on UIView called UIView+Nib, which implements the following method:

+ (UIView *)viewWithNibName:(NSString *)nibName owner:(id)owner {
    return [[[UINib nibWithNibName:nibName bundle:nil]
            instantiateWithOwner:owner options:nil]
            objectAtIndex:0];
}

So the above code can be simplified to:

[self addSubview:[UIView viewWithNibName:@"CustomView" owner:self]];

Note also that the above code can be refactored even more, since the logic is exactly the same in initWithFrame: and initWithCoder:. Hope that helps!

like image 92
Dr. Acula Avatar answered Oct 26 '22 13:10

Dr. Acula


As in Dr. Acula's answer, This is probably because custom view's nib is lazy loaded when loaded from another nib (Nested nib loading), so we need to instantiate it manually. In swift code will look like this :

    override func awakeFromNib() {
    super.awakeFromNib()
        self.customview = UINib(nibName: "CustomViewNib", bundle: nil).instantiateWithOwner(self, options:nil)[0] as!  UIView
        self.customview?.frame = self.viewContainer.bounds
        self.viewContainer.addSubview(self.customview!)
}
like image 44
Mostafa Abdellateef Avatar answered Oct 26 '22 15:10

Mostafa Abdellateef