QED
QED

Reputation: 9913

How to "import" a UIView XIB into a UIViewController XIB?

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

Answers (2)

rob mayoff
rob mayoff

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

David Hoerl
David Hoerl

Reputation: 41622

ORIGINAL: override initWithCoder, a bad idea as the comment below says

NEW SUGGESTION:

  • create the MyVC view using the technique you have now (the MyView instances will essentially be empty views)

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

Related Questions