Uibezierpath: How to Add a Border Around a View With Rounded Corners

UIBezierPath Star with rounded corners

Here is a custom UIView that computes a path for a 5-point star with a cornerRadius:

class RoundedStar : UIView {
var cornerRadius: CGFloat = 10 { didSet { setNeedsDisplay() } }
var rotation: CGFloat = 54 { didSet { setNeedsDisplay() } }
var fillColor = UIColor.red { didSet { setNeedsDisplay() } }

override func draw(_ rect: CGRect) {
let path = UIBezierPath()
let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
let r = rect.width / 2
let rc = cornerRadius
let rn = r * 0.95 - rc

var cangle = rotation
for i in 1 ... 5 {
// compute center point of tip arc
let cc = CGPoint(x: center.x + rn * cos(cangle * .pi / 180), y: center.y + rn * sin(cangle * .pi / 180))

// compute tangent point along tip arc
let p = CGPoint(x: cc.x + rc * cos((cangle - 72) * .pi / 180), y: cc.y + rc * sin((cangle - 72) * .pi / 180))

if i == 1 {
path.move(to: p)
} else {
path.addLine(to: p)
}

// add 144 degree arc to draw the corner
path.addArc(withCenter: cc, radius: rc, startAngle: (cangle - 72) * .pi / 180, endAngle: (cangle + 72) * .pi / 180, clockwise: true)

cangle += 144
}

path.close()
fillColor.setFill()
path.fill()
}
}

Example run in the Playground:

Sample Image


Simulator Demo

Sample Image

Code:

class ViewController: UIViewController {

@IBOutlet weak var rstar: RoundedStar!
@IBOutlet weak var cornerRadiusLabel: UILabel!
@IBOutlet weak var rotationlabel: UILabel!

@IBAction func cornerRadiusChanged(_ sender: UISlider) {
rstar.cornerRadius = CGFloat(sender.value)
cornerRadiusLabel.text = "Corner Radius: \(Int(sender.value))"
}

@IBAction func rotationChanged(_ sender: UISlider) {
rstar.rotation = CGFloat(sender.value)
rotationlabel.text = "Rotation: \(Int(sender.value))"
}
}

Drawing Rounded Corners Using UIBezierPath

Don't use a shape layer. Use a layer (or a view). Draw the UIBezierPath's path into it and stroke it, and then erase the bottom line by drawing it and stroking it with a .clear blend mode.

Result:

Sample Image

Code (modify as desired; I use here a clear UIView that draws the shape as its draw code):

let p = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 4.0, height: 4.0))
UIColor.white.setStroke()
p.stroke()
let p2 = UIBezierPath()
p2.move(to: CGPoint(x:0, y:self.bounds.height))
p2.addLine(to: CGPoint(x:self.bounds.width, y:self.bounds.height))
p2.lineWidth = 2
p2.stroke(with: .clear, alpha: 1)

EDIT Another way would have been to clip out the bottom line area before drawing the rounded rect:

let p1 = UIBezierPath(rect: CGRect(origin:.zero,
size:CGSize(width:self.bounds.width, height:self.bounds.height-2)))
p1.addClip()
let p = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 4.0, height: 4.0))
UIColor.white.setStroke()
p.stroke()

Round top corners of a UIView and add border

The mask layer doesn't get drawn, just used to compute the mask. Try:

-(void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius
{
CGRect bounds = self.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.path = maskPath.CGPath;

self.layer.mask = maskLayer;

CAShapeLayer* frameLayer = [CAShapeLayer layer];
frameLayer.frame = bounds;
frameLayer.path = maskPath.CGPath;
frameLayer.strokeColor = [UIColor redColor].CGColor;
frameLayer.fillColor = nil;

[self.layer addSublayer:frameLayer];
}

-(void)roundTopCornersRadius:(CGFloat)radius
{
[self roundCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) radius:radius];
}

-(void)roundBottomCornersRadius:(CGFloat)radius
{
[self roundCorners:(UIRectCornerBottomLeft|UIRectCornerBottomRight) radius:radius];
}

The frame you're currently seeing drawn is the UITextField's normal frame, so set the frame style to none. You'll also have to adjust the insets to make up for the fact that with the frame style set to none there's normally no inset.

UIBezierPath - Add rounded corner

Include the following.

circle.lineCap = kCALineJoinRound;

For Swift 3.0 and above

circle.lineCapStyle = .Round;

UIView with round corner and white border

Using the same layer object:

view.layer.borderWidth = 1;
view.layer.borderColor = [[UIColor whiteColor] CGColor];

Rounding and adding a border to a UIImage - Not UIImageView

Use this Extension

 extension UIImage { 
var rounded: UIImage? {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
defer { UIGraphicsEndImageContext() }
let bezierPath = UIBezierPath(roundedRect: rect, cornerRadius: 40)
bezierPath.addClip()
bezierPath.lineWidth = 10
draw(in: rect)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
context.setStrokeColor(UIColor.red.cgColor)
bezierPath.lineWidth = 10
bezierPath.stroke()
return UIGraphicsGetImageFromCurrentImageContext() }
}

How to add rounded corner to a UIBezierPath custom rectangle?

Instead of starting the code with a straight line :

path.moveToPoint(CGPoint(x: 300, y: 0))

I instead start with an arc (upper right):

path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner

and by doing this, I have four rounded corners and I just need to add a straight line at the end of the code right before:

path.closePath()  

Here is the code and a screenshot:

let path = UIBezierPath()
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2) , clockwise: true) //1st rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
// little triangle
path.addLineToPoint(CGPoint(x:240 , y:0))
path.addLineToPoint(CGPoint(x: 245, y: -10))
path.addLineToPoint(CGPoint(x:250, y: 0))
path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
path.addLineToPoint(CGPoint(x:300 , y:50))
path.closePath()

Sample Image



Related Topics



Leave a reply



Submit