cornerRadius with border: Glitch around border
I tried many solution and end by using UIBezierPath
.
I create category of UIView
and add method to make round rect and border.
This is method of that 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's 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 get idea of using UIBezierPath
from this amazing article: Thinking like a Bézier path
I get most of code from this two link:
- UIView category for rounding just the corners which you want, not all like CALayer cornerRadius.
- How to get a border on UIBezierPath
Note: This is category method so self represent view on which this method is called. Like UIButton, UIImageView etc.
Here is a Swift 5 version of @CRDave's answer 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's 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) }}
This is Kamil Nomtek.com's answer updated to Swift 3+ and with some refinements (mostly semantics/naming and using a 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's 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) }}