Reputation: 9913
I've seen a few answers here that seem to get at what I'm after, but I can't make any of them work.
MyView
is a UIView subclass with a handful of images and text fields laid out and styled in MyView.xib
and bound to IBOutlets in MyView.(h|m)
. File's Owner
is not set in the XIB, but MyView is the class of the top-level object. At runtime I dynamically load various MyViews into certain view controllers via NSBundle's
loadNibNamed
. Everything goes swimmingly.
But I have another view controller, MyVC
, which at design time I would like to populate with a few instances of MyView. Following intuition, I laid out a UIView object in MyVC.xib
with the same size as MyView, set its type to MyView
, added an IBOutlet declaration in MyVC.h
, and hooked up the connection.
When I run, MyVC
shows no MyView
. But debugging indicates that the connection is made, the view is indeed a MyView
, and MyView's
awakeFromNib
does execute. And I'm sure that MyVC's
MyView
is on top of the hierarchy, because I set its background to a nice bright red. So where the devil is MyView
?
Upvotes: 2
Views: 1319
Reputation: 385500
The simplest solution is to make MyView
have a single subview, which is a container for the rest of its descendant views. This gives you a single point of contact between what's in MyVC.xib
and what's in MyView.xib
, and lets you connect outlets in both xibs.
In MyVC.xib
, set the class of each placeholder view to MyView
.
In MyView.xib
, set the class of the top-level view to UIView
. Set the class of File's Owner to MyView
. If you have any outlets on MyView
that were connected in MyView.xib
, you will need to reconnect them to File's Owner since the top-level view no longer has those outlets.
In -[MyView initWithCoder:]
, load MyView.xib
and add its top-level view as your subview. Untested example:
+ (UINib *)nib {
static dispatch_once_t once;
static UINib *nib;
dispatch_once(&once, ^{
nib = [UINib nibWithNibName:NSStringFromClass(self) bundle:[NSBundle bundleForClass:self]];
});
return nib;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
NSArray *contents = [[self.class nib] instantiateWithOwner:self options:nil][0];
UIView *containerView = contents[0];
// Make sure the container view's size tracks my size.
containerView.frame = self.bounds;
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.autoresizesSubviews = YES;
if ([self respondsToSelector:@selector(setTranslatesAutoresizingMaskIntoConstraints:)]) {
self.translatesAutoresizingMaskIntoConstraints = YES;
containerView.translatesAutoresizingMaskIntoConstraints = YES;
}
// If you're using autolayout in both xibs, you should probably create
// constraints between self and containerView here.
[self addSubview:containerView];
}
return self;
}
This has the effect that you can connect MyView
's outlets to things in MyVC.xib
and to things in MyView.xib
, and you can connect the outlets of other objects in MyVC.xib
and MyView.xib
to the instance of MyView
. However, you can't connect outlets of other objects in MyVC.xib
to other objects in MyView.xib
or vice versa.
Upvotes: 1
Reputation: 41622
ORIGINAL: override initWithCoder, a bad idea as the comment below says
NEW SUGGESTION:
Then in the MyVC's viewDidLoad: method:
create new MyView instances using your nib, one for each of the ones in MyView
replace each of the empty (ie placeholder) MyViews with the ones you just created, making sure the frames are properly set (set any property/ivars, and use '- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2' to replace the old view with the new one in the subview array)
Upvotes: 0