Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom view created with Interface Builder does not render when called in other views

I have an xib for the main window, and I created a custom view in the following steps:

  1. Create a new class which inherits from NSView.

MyView.h:

    #import <Cocoa/Cocoa.h>
    IB_DESIGNABLE
    @interface MyView : NSTableCellView
    @end

MyView.m:

    #import "MyView.h"

    @implementation MyView

    - (void)awakeFromNib {
        NSLog(@"Post view awaking from nib.");
    }

    @end
  1. Create a new xib, and set the root view's class to the class created above. And design in that xib.
  2. Set outlets from the xib to the class.

And I tried to use this custom view in the main window in the following steps:

  1. Drag a custom view to the main window's xib.
  2. Set the class of that custom view to the class created above.

But nothing renders. From the log, I can see that code in awakeFromNib from the custom view class is executed. When I set the class to be IB_DESIGNABLE, the view gets empty in the main window's xib, different from what I designed.

I tried to set the file owner of the custom view's xib to the custom class, but nothing changed.

I guess the problem is that, the custom view's xib file is not actually loaded. When I googled it, there seem to be few references on this exact topic. So, how should I actually achieve this goal? I.e., design a view in IB, implement its methods in a class, associate these two, and expose it just like a system view for use in other xibs?

UPDATE:

I found a tutorial and realized what I lack (for correctly rendering the view when built). I have to add an outlet from the view in the xib to the view class:

@property (nonatomic, strong) IBOutlet NSView *view;

, and then load it in the (id)initWithCoder:(NSCoder *)coder method.

[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self topLevelObjects:nil];
[self addSubview:self.view];

But the view still won't render in the interface builder.

like image 525
Colliot Avatar asked Oct 25 '15 11:10

Colliot


2 Answers

Your guess is correct: the xib is not being loaded. The nib loader doesn't know about your custom view's nib. The nib framework doesn't provide a facility for defining that connection, so you need to write code to load the xib.

Here's what I'd do. Add a contentView property to your custom view:

@interface MyView ()

@property (nonatomic, strong, readwrite) IBOutlet NSView *contentView;

@end

In your custom view's nib, set the custom class of the root view back to NSView and disconnect all the (no-longer-valid) outlet connections from it. Set the custom class of File's Owner to your custom class name (e.g. MyView). Connect the root view to File's Owner's contentView outlet, and connect all the other outlets from File's Owner to the appropriate objects in the nib.

Then implement awakeFromNib in your custom view subclass to load the nib and add the content view as a subview:

@implementation MyView {
    BOOL hasLoadedOwnNib: 1;
}

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

- (void)loadOwnNibIfNeeded {
    if (hasLoadedOwnNib) {
        return;
    }

    hasLoadedOwnNib = YES;

    [[NSBundle bundleForClass:self.class] loadNibNamed:NSStringFromClass(self.class) owner:self topLevelObjects:nil];
    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
    [self addSubview:self.contentView];
}

@end

Note that you have to be careful not to allow infinite recursion. When your app loads the main window's nib, it will create an instance of MyView and (eventually) send it awakeFromNib. Then, in awakeFromNib, MyView loads its own nib, where it is the File's Owner. The nib loader sends awakeFromNib to File's Owner, and this will happen while you're already in -[MyView awakeFromNib]. If you don't check for this, you get a stack overflow due to unbounded recursion.

like image 88
rob mayoff Avatar answered Oct 28 '22 06:10

rob mayoff


You aren't providing any code, but here are some sanity checks:

  • Are you specifying the nib name correctly? In iOS its caps sensitive, but I don't think it is for you.

  • Check the package, is the nib actually there? Make sure it is part of the target you are building.

  • Could also be a frame issue. Make sure your auto-resizing parameters are set up correctly and that everything is in frame.

  • Another check you can do is set your IBOutlets to the actual frame of a UIView (or other) that you are interested in. Then in awakeFromNib, you can make sure their frame exists, or that they exist at all.

like image 22
Kelsey Avatar answered Oct 28 '22 04:10

Kelsey