I have an xib for the main window, and I created a custom view in the following steps:
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
And I tried to use this custom view in the main window in the following steps:
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.
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.
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.
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