15

I am trying to round the bottom two corners of a UIView, and have the layer’s border show up rounded as well. I am currently doing:

UIRectCorners corners = UIRectCornerBottomLeft | UIRectCornerBottomRight;
CGSize radii = CGSizeMake(kThisViewCornerRadius, kThisViewCornerRadius);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:myView.bounds
                                           byRoundingCorners:corners
                                                 cornerRadii:radii];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setPath:path.CGPath];
myView.layer.mask = maskLayer;

This works fine for normal views. However, myView’s layer has its borderColor and borderWidth set, and as you can see from the screenshot, the layer’s border is not getting rounded:

Screenshot of the problem

I also tried subclassing UIView and returning [CAShapeLayer layer] from +[Class layerClass], and setting up the view’s layer as a shape layer, but the border ended up beneath the view’s subviews.

Is it possible to round some of a view’s corners, round the view’s layer’s border, and clip the subviews underneath the layer’s border?

Note that this is not about how to round some corners and not others, but rather how to get the stroke to behave correctly.

Zev Eisenberg
  • 8,080
  • 5
  • 38
  • 82
  • 1
    Add a shape layer that is stroked into the layer hierarchy? – David Rönnqvist Feb 14 '13 at 01:51
  • Good suggestion, but I tested it and the shape layer’s stroke still shows up behind the view’s subviews. – Zev Eisenberg Feb 14 '13 at 04:54
  • Actually, your comment got me thinking on the right track, and I ended up getting it. I’ll post it as an answer. Thanks! – Zev Eisenberg Feb 14 '13 at 04:59
  • possible duplicate of [Rounded UIView using CALayers - only some corners - How?](http://stackoverflow.com/questions/2264083/rounded-uiview-using-calayers-only-some-corners-how) – Rivera May 21 '15 at 12:35

3 Answers3

36

I figured out a new way of thinking about it, thanks to David Rönnqvist’s comment.

I was trying to do the corner rounding and the stroke all in one layer. Instead, I broke it up into two layers: one to mask the view’s layer to round the corners, and the other in a view to add the stroke.

UIView *containerView = [[UIView alloc] initWithFrame:someFrame];

UIRectCorners corners = UIRectCornerBottomLeft | UIRectCornerBottomRight;
CGSize radii = CGSizeMake(kThisViewCornerRadius, kThisViewCornerRadius);

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:myView.bounds
                                           byRoundingCorners:corners
                                                 cornerRadii:radii];

// Mask the container view’s layer to round the corners.
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
containerView.layer.mask = cornerMaskLayer;

// Make a transparent, stroked layer which will dispay the stroke.
CAShapeLayer *strokeLayer = [CAShapeLayer layer];
strokeLayer.path = path.CGPath;
strokeLayer.fillColor = [UIColor clearColor].CGColor;
strokeLayer.strokeColor = [UIColor redColor].CGColor;
strokeLayer.lineWidth = 2; // the stroke splits the width evenly inside and outside,
                           // but the outside part will be clipped by the containerView’s mask.

// Transparent view that will contain the stroke layer
UIView *strokeView = [[UIView alloc] initWithFrame:containerView.bounds];
strokeView.userInteractionEnabled = NO; // in case your container view contains controls
[strokeView.layer addSublayer:strokeLayer];

// configure and add any subviews to the container view

// stroke view goes in last, above all the subviews
[containerView addSubview:strokeView];
Community
  • 1
  • 1
Zev Eisenberg
  • 8,080
  • 5
  • 38
  • 82
11

Zev Eisenberg's answer is the right one.

Although I prefer:

[self.layer addSublayer:strokeLayer];

instead of creating and adding a subview:

CGSize radii = CGSizeMake(radius, radius);

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                           byRoundingCorners:corners
                                                 cornerRadii:radii];

// Mask the container view’s layer to round the corners.
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
self.layer.mask = cornerMaskLayer;

// Make a transparent, stroked layer which will dispay the stroke.
CAShapeLayer *strokeLayer = [CAShapeLayer layer];
strokeLayer.path = path.CGPath;
strokeLayer.fillColor = [UIColor clearColor].CGColor;
strokeLayer.strokeColor = color.CGColor;
strokeLayer.lineWidth = 2; // the stroke splits the width evenly inside and outside,
// but the outside part will be clipped by the containerView’s mask.

[self.layer addSublayer:strokeLayer];
Andrei Radulescu
  • 1,989
  • 1
  • 17
  • 29
0

Here is the small code. Alloc init a view and send to this method to get corners rounded. You can optionally round any of the corners u want. Also give shadow stroke color.

 -(void) setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners withColor:  (UIColor*) color
{
 UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds  byRoundingCorners:corners cornerRadii:CGSizeMake(9.0, 9.0)];

CAShapeLayer* shape = [[[CAShapeLayer alloc] init] autorelease];
[shape setPath:rounded.CGPath];
shape.strokeColor = [[UIColor grayColor] CGColor];

view.backgroundColor=color;
view.layer.mask = shape;
}

Call the method like this.

[self setMaskTo:ABCView byRoundingCorners:UIRectCornerAllCorners withColor:[UIColor greenColor]];
NaXir
  • 2,373
  • 2
  • 24
  • 31