21

I am creating a NSView subclass that has rounded corners. This view is meant to be a container and other subviews will be added to it. I am trying to get the rounded corners of the NSView to clip all of the subview's corners as well, but am not able to get it.

- (void)drawRect:(NSRect)dirtyRect {
    NSRect rect = [self bounds];
    NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:self.radius yRadius:self.radius];
    [path addClip];

    [[NSColor redColor] set];
    NSRectFill(dirtyRect);

    [super drawRect:dirtyRect];     
}

The red is just for example. If I add a subview to the rect, The corners are not clipped: enter image description here

How can I achieve this?

coneybeare
  • 33,113
  • 21
  • 131
  • 183
  • 1
    The clip in -drawRect: affects *only* the view's drawing and has nothing to do with the subviews that still exist in the view's entire rectangle. The drawing isn't *over* the subviews, it's *under* them. Jason's answer is about the only way to accomplish this. Use layer-backed views. – Joshua Nozzi Oct 06 '11 at 15:29

3 Answers3

33

Using Core Animation layers will clip sublayers correctly.

In your container NSView subclass:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.layer = _layer;   // strangely necessary
        self.wantsLayer = YES;
        self.layer.masksToBounds = YES;    
        self.layer.cornerRadius = 10.0;    
    }    
    return self;
}
mxcl
  • 26,392
  • 12
  • 99
  • 98
Jason Harwig
  • 43,743
  • 5
  • 43
  • 44
  • 3
    Note: This won't work for the special case of rounding the corners of an NSScrollView whose document view grows in size past the bounds of the NSScrollView (it appears to reset the layer clipping path once the document bounds change). However, it will work if the document view is a static size less than or equal to the scroll view size. – Dalmazio Mar 21 '12 at 08:08
  • 2
    Before calling self.wantsLayer = YES; you need to set the layer: self.layer = layer; http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/Reference/NSView.html#//apple_ref/doc/uid/20000014-SW61 – Jason Fuerstenberg Feb 13 '13 at 08:02
  • 1
    @JasonFuerstenberg `self.wantsLayer = YES;` will automatically create a `CALayer` and assign it to the views `layer` property. There's really no reason to do this. – IluTov Nov 29 '13 at 19:42
24

You can do it in the interface builder without subclassing adding User Defined Runtime Attributes"

enter image description here

Tibidabo
  • 21,461
  • 5
  • 90
  • 86
0

Have you tried clipping with layers?

self.layer.cornerRadius = self.radius; self.layer.masksToBounds = YES;


Ah, sorry, somehow I've missed that you were talking about NSView, not UIView. It would be hard to clip NSView subviews in all cases because it seems that most of Cocoa standard views set their own clipping path. It might be easier to layout subviews with some paddings and avoid need for clipping.

chown
  • 51,908
  • 16
  • 134
  • 170
tundrabot
  • 341
  • 2
  • 3
  • Yes. Adding theses lines, as well as [self setWantsLayer:YES], does not round the corners of the container or its contents – coneybeare Feb 23 '11 at 13:23
  • Ah, sorry, somehow I've missed that you were talking about NSView, not UIView. It would be hard to clip NSView subviews in all cases because it seems that most of Cocoa standard views set their own clipping path. It might be easier to layout subviews with some paddings and avoid need for clipping. – tundrabot Feb 23 '11 at 14:11
  • I rethought my layout. Marked correct for that, but you should edit your answer to include what you said in the comment, not the incorrect layer stuff to avoid confusion – coneybeare Feb 23 '11 at 23:04
  • I _so_ want to upvote this answer because it helped me with my UIView. – Tom Howard May 06 '16 at 15:18