How to add an NSView to NSWindow in a Cocoa app?
Solution 1
Use setWantsLayer:
method of NSView class.
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)];
[view setWantsLayer:YES];
view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
[self.window.contentView addSubview:view];
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview:pushButton];
NSLog(@"subviews are %@", [self.window.contentView subviews]);
Solution 2
To expand on the suggestion by Kevin Ballard, the classic way to do this is to subclass NSView
and override the -drawRect:
method. NSRectFill
is a very convenient function for filling a rectangle without having to create a bezier path:
- (void)drawRect:(NSRect)rect
{
[[NSColor yellowColor] set];
NSRectFill(rect);
}
Solution 3
NSView
s in Cocoa are, by default, not layer-backed. I suspect that if you type
NSLog(@"%@", view.layer);
you will see that it is nil
.
In iOS, all views have layers. But on OS X, views don't have layers. In addition, there's 2 "modes" of layer-backed views on OS X. There's what's called a "layer-backed views" and a "layer-hosting view". A layer-backed view uses a CoreAnimation layer to cache drawn data, but you are not allowed to interact with the layer in any way. A layer-hosting view uses a CALayer
that you explicitly provide, and you may mess with that layer all you want. However, with a layer-hosting view you may not add any subviews, or use the built-in NSView
drawing mechanism. A layer-hosting view must only be used as the root of a CoreAnimation layer hierarchy.
Given all this, you should probably avoid using CoreAnimation at all for your view.
It's possible that an NSBox
will do what you want. You can certainly set a fill color there, turn off the border, and set the style to custom. I'm just not 100% certain it will draw as a simple filled rectangle of color. Alternatively you can define your own NSView
subclass that draws a color in -drawRect:
.
Jeremy L
Updated on July 05, 2022Comments
-
Jeremy L almost 2 years
Since the template of an OS X app in Xcode seems to be similar to an empty app template, the following is used to add a view and a button (trying not to use Interface builder for now):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)]; view.layer.backgroundColor = [[NSColor yellowColor] CGColor]; [self.window.contentView addSubview:view]; NSRect frame = NSMakeRect(10, 40, 90, 40); NSButton* pushButton = [[NSButton alloc] initWithFrame: frame]; pushButton.bezelStyle = NSRoundedBezelStyle; [self.window.contentView addSubview:pushButton]; NSLog(@"subviews are %@", [self.window.contentView subviews]); }
Similar code on iOS should have produced a yellow box and a button, but the code above only produce a button, but the view won't show. Is there something wrong with the code above, and how to make it show the view with a yellow background?
-
Jeremy L over 11 yearsdo you know how to make the view show with a yellow background?
-
Lily Ballard over 11 years@JeremyL: I'm researching that right now. I primarily do iOS, so please hold on.
-
Jeremy L over 11 yearsNSView doesn't have a backgroundColor property, it seems. I kind of miss
UIKit
...! -
rdelmar over 11 years@JeremyL, I always do as Kevin mentioned, use an NSBox instead of a custom view. Just set its type to custom, and then you can (in IB or code) set its background color, a border color (or no border if you choose), and round corners if you wish.
-
Lily Ballard over 11 yearsThis answer is bad. You are explicitly told not to touch the backing layer in a layer-backed view (a view where you used
-setWantsLayer:
without calling-setLayer:
first). From the docs: When using layer-backed views you should never interact directly with the layer -
Jeremy L over 11 yearsyou mean, should first instantiate a CALayer, set it to view.layer, and then set wantsLayer to YES, and set background color?
-
Jeremy L over 11 yearsI tried an NSBox, but it has a "title" with it, and there is no backgroundColor property
-
Lily Ballard over 11 years@JeremyL: You can nil out the title, set the box type to custom, set the border type to none. The background color is then controlled by the
fillColor
. I would encourage you to create a xib, drag an NSBox onto your canvas, and play with the attributes there. -
Lily Ballard over 11 years@JeremyL: No, because a layer-hosting view (which is what you get if you set the layer) cannot have any subviews. If you want your button to be on top of the background, it needs to be a subview (overlapping views that aren't hierarchical may not work properly, and almost certainly won't if one is layer-hosting). Since your button would need to be a subview, the yellow background cannot be a layer-hosting view.
-
rdelmar over 11 years@JeremyL, you tried a box, but didn't set its type to custom -- the custom box has no title and does have fill color, border color and border type.
-
Hope about 8 yearsHow do you link a custom class to that view ? @NSAddict