CornerRadius with border: crashing around the border

My application is mostly round and border.

I use the layer property of the UIView to get the angular radius and border.

But I ran into the problem that the angles are not clear.

I get the following results:

UIButton

screenshot of a button with rounded corners and a border

UIImageView

screenshot of an image view with rounded corners and a border

You can observe a thin line border around a white or gray border.

This is my code:

 button.layer.borderWidth = 2.0; button.layer.borderColor = [[UIColor whiteColor] CGColor]; button.layer.cornerRadius = 4; button.clipsToBounds = YES; 

I was looking for a solution, but I did not succeed.

I tried button.layer.masksToBounds = YES , but with no effect.

Did I miss something? Or are there other methods that can give me better results compared to CALayer ?

+9
source share
6 answers

I tried many solutions and ended up using UIBezierPath .

I create a UIView category and add a method to create a rectangle and frame.

This is the method of this category:

 - (void)giveBorderWithCornerRadious:(CGFloat)radius borderColor:(UIColor *)borderColor andBorderWidth:(CGFloat)borderWidth { CGRect rect = self.bounds; //Make round // Create the path for to make circle UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)]; // Create the shape layer and set its path CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = rect; maskLayer.path = maskPath.CGPath; // Set the newly created shape layer as the mask for the view layer self.layer.mask = maskLayer; //Give Border //Create path for border UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)]; // Create the shape layer and set its path CAShapeLayer *borderLayer = [CAShapeLayer layer]; borderLayer.frame = rect; borderLayer.path = borderPath.CGPath; borderLayer.strokeColor = [UIColor whiteColor].CGColor; borderLayer.fillColor = [UIColor clearColor].CGColor; borderLayer.lineWidth = borderWidth; //Add this layer to give border. [[self layer] addSublayer:borderLayer]; } 

I got an idea about using UIBezierPath from this amazing article: Thinking Like a Bezier Path

I get most of the code from this link:

Note. This is a category method, so imagine the view for which this method is called. Like UIButton, UIImageView, etc.

+15
source

Here is the Swift 5 @CRDave answer version as an extension of UIView:

 protocol CornerRadius { func makeBorderWithCornerRadius(radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) } extension UIView: CornerRadius { func makeBorderWithCornerRadius(radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) { let rect = self.bounds let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)) // Create the shape layer and set its path let maskLayer = CAShapeLayer() maskLayer.frame = rect maskLayer.path = maskPath.cgPath // Set the newly created shape layer as the mask for the view layer self.layer.mask = maskLayer // Create path for border let borderPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)) // Create the shape layer and set its path let borderLayer = CAShapeLayer() borderLayer.frame = rect borderLayer.path = borderPath.cgPath borderLayer.strokeColor = borderColor.cgColor borderLayer.fillColor = UIColor.clear.cgColor borderLayer.lineWidth = borderWidth * UIScreen.main.scale //Add this layer to give border. self.layer.addSublayer(borderLayer) } } 
+10
source

This is Kamil Nomtek.com's answer, upgraded to Swift 3+ and with some refinements (mainly semantics / name and use of class protocol).

 protocol RoundedBorderProtocol: class { func makeBorder(with radius: CGFloat, borderWidth: CGFloat, borderColor: UIColor) } extension UIView: RoundedBorderProtocol { func makeBorder(with radius: CGFloat, borderWidth: CGFloat, borderColor: UIColor) { let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)) // Create the shape layer and set its path let maskLayer = CAShapeLayer() maskLayer.frame = bounds maskLayer.path = maskPath.cgPath // Set the newly created shape layer as the mask for the view layer layer.mask = maskLayer //Create path for border let borderPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)) // Create the shape layer and set its path let borderLayer = CAShapeLayer() borderLayer.frame = bounds borderLayer.path = borderPath.cgPath borderLayer.strokeColor = borderColor.cgColor borderLayer.fillColor = UIColor.clear.cgColor //The border is in the center of the path, so only the inside is visible. //Since only half of the line is visible, we need to multiply our width by 2. borderLayer.lineWidth = borderWidth * 2 //Add this layer to display the border layer.addSublayer(borderLayer) } } 
+7
source

The answer from CRDave works great, but it has one drawback: when called repeatedly, as when resizing, it continues to add layers. Instead, previous layers should be updated.
See below for an updated version of ObjC. For quick, please adapt accordingly.

 // UIView+Border.h #import <UIKit/UIKit.h> @interface UIView (Border) - (void)setBorderWithCornerRadius:(CGFloat)radius color:(UIColor *)borderColor width:(CGFloat)borderWidth; @end // UIView+Border.m #import "UIView+Border.h" @implementation UIView (Border) - (void)setBorderWithCornerRadius:(CGFloat)radius color:(UIColor *)borderColor width:(CGFloat)borderWidth { CGRect rect = self.bounds; //Make round // Create the path for to make circle UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)]; // Create the shape layer and set its path CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = rect; maskLayer.path = maskPath.CGPath; // Set the newly created shape layer as the mask for the view layer self.layer.mask = maskLayer; //Give Border //Create path for border UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)]; // Create the shape layer and set its path NSString *layerName = @"ig_border_layer"; CAShapeLayer *borderLayer = (CAShapeLayer *)[[[self.layer sublayers] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"name == %@", layerName]] firstObject]; if (!borderLayer) { borderLayer = [CAShapeLayer layer]; [borderLayer setName:layerName]; //Add this layer to give border. [[self layer] addSublayer:borderLayer]; } borderLayer.frame = rect; borderLayer.path = borderPath.CGPath; borderLayer.strokeColor = [UIColor whiteColor].CGColor; borderLayer.fillColor = [UIColor clearColor].CGColor; borderLayer.lineWidth = borderWidth; } @end 
+3
source

de. The answer worked much better for me than the CRDave answer.

I had to translate it from Swift, so I decided to go ahead and publish the translation:

 extension UIView { func giveBorderWithCornerRadius(cornerRadius r: CGFloat, borderColor c: UIColor, strokeWidth w: CGFloat) { let rect = self.bounds let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: r, height: r)) let maskLayer = CAShapeLayer() maskLayer.frame = rect maskLayer.path = maskPath.cgPath self.layer.mask = maskLayer let borderPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: r, height: r)) let layerName = "border_layer" var borderLayer: CAShapeLayer? = self.layer.sublayers?.filter({ (c) -> Bool in if c.name == layerName { return true } else { return false } }).first as? CAShapeLayer if borderLayer == nil { borderLayer = CAShapeLayer() borderLayer!.name = layerName self.layer.addSublayer(borderLayer!) } borderLayer!.frame = rect borderLayer!.path = borderPath.cgPath borderLayer!.strokeColor = c.cgColor borderLayer!.fillColor = UIColor.clear.cgColor borderLayer!.lineWidth = w } } 

I have the layoutSubviews() method from layoutSubviews()

0
source

remove

 button.layer.borderWidth = 0.3; button.layer.borderColor = [[UIColor blueMain] CGColor]; 
-3
source

Source: https://habr.com/ru/post/974485/


All Articles