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:
UIView
subclass, CustomView
..xib
file with three subviewsUIView
onto the view, and then changed the custom class to CustomView
.CustomView
nib in IB to a custom class CustomView
and connecting the IBOutlets
to the view, but they were still nil.CustomView
and connecting the IBOutlets
to file's owner, but they were still nil. 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";
}
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!
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!)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With