Add curve on arc swift - ios

I want to generate a curve in the middle of a line programatically, however the edge of the curve is not rounded using my current solution.
func createPath() -> CGPath {
let height: CGFloat = 86.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: (centerWidth - (height/2)), y: 0))
path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: 80 / 2, startAngle: 180 * CGFloat(PI)/180, endAngle: 0 * CGFloat(PI)/180, clockwise: false)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.lineCapStyle = .round
path.stroke()
path.close()
self.path = path
return path.cgPath
}
This is the Arc I have created but I want the curves to be rounded:
But I want it to be like this:

This is the code that you need
func createPath() -> CGPath {
let padding: CGFloat = 5.0
let centerButtonHeight: CGFloat = 53.0
let f = CGFloat(centerButtonHeight / 2.0) + padding
let h = frame.height
let w = frame.width
let halfW = frame.width/2.0
let r = CGFloat(18)
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0))
path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0))
path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2.0), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))
path.addLine(to: CGPoint(x: w, y: 0))
path.addLine(to: CGPoint(x: w, y: h))
path.addLine(to: CGPoint(x: 0.0, y: h))
path.close()
return path.cgPath
}
Result:
You can customize the size of the button...
For a great explanation of how it is work, I recommend you to read this explanation, and you can draw any shape which do you want: https://ayusinghi96.medium.com/draw-custom-shapes-and-views-with-uiberzierpath-ios-1737f5cb975

You can just draw more arc like you already does. Or may be you can use path.addCurve, if you dont want oval
func createPath() -> CGPath {
let bigRadius: CGFloat = 40.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
let radius: CGFloat = 4 //change it if you want
let leftArcOriginX = centerWidth - bigRadius - radius
let leftArcOriginY: CGFloat = 0
path.addLine(to: CGPoint(x: leftArcOriginX, y: leftArcOriginY))
// add left little arc, change angle if you want, if you dont want oval, may be you can use path.addCurve(to: , controlPoint1: , controlPoint2: )
path.addArc(withCenter: CGPoint(x: leftArcOriginX, y: leftArcOriginY + radius), radius: radius, startAngle: CGFloat(270.0 * Double.pi/180.0), endAngle: 0, clockwise: true)
// add big arc
path.addArc(withCenter: CGPoint(x: centerWidth, y: radius), radius: bigRadius, startAngle: CGFloat(180.0 * Double.pi/180.0), endAngle: CGFloat(0 * Double.pi/180.0), clockwise: false)
// add right litte arc
path.addArc(withCenter: CGPoint(x: centerWidth + bigRadius + radius, y: radius), radius: radius, startAngle: CGFloat(180.0 * Double.pi/180.0), endAngle: CGFloat(270.0 * Double.pi/180.0), clockwise: true)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.lineCapStyle = .round
path.stroke()
path.close()
return path.cgPath
}

Related

Shape in ios swift

how to draw the curve with blur background?
here I have attached my code but am not getting the exact output
#IBDesignable class CurvedHeaderView: UIView {
#IBInspectable var curveHeight:CGFloat = 50.0
var curvedLayer = CAShapeLayer()
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.move(to: CGPoint(x: 0, y: rect.height))
// path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addArc(withCenter: CGPoint(x: CGFloat(rect.width) - curveHeight, y: 100), radius: curveHeight, startAngle: 0, endAngle: 1.5 * CGFloat.pi, clockwise: false)
// path.addArc(withCenter: CGPoint(x: CGFloat(rect.width) - curveHeight, y: rect.height), radius: curveHeight, startAngle: 0, endAngle: 1.5 * CGFloat.pi, clockwise: false)
// path.addLine(to: CGPoint(x: curveHeight, y: rect.height - curveHeight))
// path.addLine(to: CGPoint(x: curveHeight, y: 50))
path.addLine(to: CGPoint(x: curveHeight, y: curveHeight))
path.addArc(withCenter: CGPoint(x: curveHeight, y: 0), radius: curveHeight, startAngle: 0, endAngle: CGFloat.pi, clockwise: true)
path.addLine(to: CGPoint(x: 0, y: 0))
// path.addArc(withCenter: CGPoint(x: curveHeight, y: rect.height - (curveHeight * 2.0)), radius: curveHeight, startAngle: 0, endAngle: CGFloat.pi, clockwise: true)
path.close()
curvedLayer.path = path.cgPath
curvedLayer.fillColor = UIColor.white.cgColor
curvedLayer.frame = rect
self.layer.insertSublayer(curvedLayer, at: 0)
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowRadius = 10.0
self.layer.shadowOpacity = 0.7
}
}
Please share me reference if anything wrong
Thanks in advance
I have tried but it showing code working but not exactly been getting
#IBOutlet weak var blurEffectView: UIVisualEffectView!
override func viewDidLayoutSubviews() {
let maskView = CurvedHeaderView(frame: blurEffectView.bounds)
maskView.clipsToBounds = true;
maskView.backgroundColor = UIColor.clear
blurEffectView.mask = maskView
}

How to do this shape with UIBezierPath

I'm trying to convert my UIView to this shape with UIBezierPath, currently I'm only able to do the left bottom corner, seeking for help for adding other corners.
Code for left bottom only.
let mask = CAShapeLayer()
mask.frame = self.innerLayout.layer.bounds
let path = UIBezierPath()
let radius: CGFloat = 50
let rect = mask.bounds
path.move(to: rect.origin)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: 0, endAngle: CGFloat(M_PI_2 * 3), clockwise: false)
mask.path = path.cgPath
self.innerLayout.layer.mask = mask
I did couple of trials for adding other corners but my UIView got funny shapes. I simply added this by copying and pasting (and changing origin), I believe we will use this part of the code 4 times to add 4 corners
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: 0, endAngle: CGFloat(M_PI_2 * 3), clockwise: false)
Here's a complete example to be run in Playground. Such geometric shapes are always tricky. It's very easy to get coordinates wrong or have an arc go into the wrong direction.
import UIKit
import PlaygroundSupport
class ConcaveConrnerViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let frame = CGRect(x: 100, y: 200, width: 200, height: 200)
let subView = UIView(frame: frame)
subView.backgroundColor = .red
let mask = CAShapeLayer()
mask.frame = subView.layer.bounds
let path = UIBezierPath()
let radius: CGFloat = 50
let rect = mask.bounds
path.move(to: CGPoint(x: rect.minX + radius, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX - radius, y: rect.minY))
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 2), endAngle: CGFloat(Double.pi / 2 * 3), clockwise: false)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - radius))
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 1), endAngle: CGFloat(Double.pi / 2 * 2), clockwise: false)
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 0), endAngle: CGFloat(Double.pi / 2 * 1), clockwise: false)
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + radius))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 3), endAngle: CGFloat(Double.pi / 2 * 0), clockwise: false)
path.close()
mask.path = path.cgPath
subView.layer.mask = mask
view.addSubview(subView)
self.view = view
}
}
PlaygroundPage.current.liveView = ConcaveConrnerViewController()

Set the radius of one corner of a SKShapeNode

I want to change the radius of two corners of a SKShapeNode (rect) but I didn't find a working solution.
I've tried to use a path, and it didn't work.
Swift 4.2, iOS 12.1.1, Xcode 10.1
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: -128, y: -128, width: 256, height: 256), cornerRadius: 64).cgPath
shape.position = CGPoint(x: frame.midX, y: frame.midY)
shape.fillColor = UIColor.red
shape.strokeColor = UIColor.blue
shape.lineWidth = 10
addChild(shape)
I made a function that will allow you to customize each corner radius to whatever size you'd like. You can have 1,2,3 or 4 corners with a radius. If you always just want two corners then I would suggest making a wrapper function so you don't have so many parameters to fill in each time you call it.
func CustomRoundRectPath(_ rect:CGRect, _ TLR:CGFloat,_ TRR:CGFloat,_ BLR:CGFloat,_ BRR:CGFloat) -> CGPath {
let w = rect.width
let h = rect.height
//TLP:(TLP)
let TLP = CGPoint(x: TLR, y: h - TLR)
let TRP = CGPoint(x: w - TRR, y: h - TRR)
let BLP = CGPoint(x: BLR, y: BLR)
let BRP = CGPoint(x: w - BRR, y: BRR)
//Create path and addComponents
let path = CGMutablePath()
path.addArc(center: TLP, radius: TLR, startAngle: CGFloat.pi, endAngle: CGFloat.pi/2, clockwise: true)
path.addLine(to: CGPoint(x: TRP.x, y: h))
path.addArc(center: TRP, radius: TRR, startAngle: CGFloat.pi/2, endAngle: 0, clockwise: true)
path.addLine(to: CGPoint(x: w, y: BRP.y))
path.addArc(center: BRP, radius: BRR, startAngle: 0, endAngle: -CGFloat.pi/2, clockwise: true)
path.addLine(to: CGPoint(x: BLP.x, y: 0))
path.addArc(center: BLP, radius: BLR, startAngle: -CGFloat.pi/2, endAngle: -CGFloat.pi, clockwise: true)
path.addLine(to: CGPoint(x: 0, y: TLP.y))
return path
}

UIBezierPath Creating Triangle,Square,Circle Swift

I am attempting to create three shapes: a Circle, Square & Triangle. I have created the circle & square, but am unable to create the triangle. My biggest issue is to keep all three shapes in the center of the screen. The circle and square are fine, but when I attempt to make the triangle it does not work. I am also trying to make the triangle look like a "play button" so that the "tip" of the triangle is facing to the right. Here is the code.
func trainglePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
path.move(to: CGPoint(x: startX, y: startY))
path.addLine(to: CGPoint(x: startX, y: startY - side))
path.addLine(to: CGPoint(x: startX + side, y: startY + side/2))
path.close()
return path
}
func circlePathWithCenter(center: CGPoint, radius: CGFloat) -> UIBezierPath {
let circlePath = UIBezierPath()
circlePath.addArc(withCenter: center, radius: radius, startAngle: -CGFloat(M_PI), endAngle: -CGFloat(M_PI/2), clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: -CGFloat(M_PI/2), endAngle: 0, clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI/2), clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: CGFloat(M_PI/2), endAngle: CGFloat(M_PI), clockwise: true)
circlePath.close()
return circlePath
}
func squarePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let squarePath = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
squarePath.move(to: CGPoint(x: startX, y: startY))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX + side, y: startY))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX + side, y: startY + side))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX, y: startY + side))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.close()
return squarePath
}
Where am I going wrong with the geometry for the triangle?
You subtracted when you need to add. Remember, +Y is down.
Change:
path.addLine(to: CGPoint(x: startX, y: startY - side))
To:
path.addLine(to: CGPoint(x: startX, y: startY + side))
Here it is running in a Playground:
Here's the full code for the Playground demo:
class Custom: UIView {
override func draw(_ rect: CGRect) {
let path = trainglePathWithCenter(center: self.center, side: self.bounds.width / 2)
path.stroke()
}
func trainglePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
path.move(to: CGPoint(x: startX, y: startY))
path.addLine(to: CGPoint(x: startX, y: startY + side))
path.addLine(to: CGPoint(x: startX + side, y: startY + side/2))
path.close()
return path
}
}
let custom = Custom(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
custom.backgroundColor = .white
Result triangles
Code in swift5
//TriangleView
extension UIView {
func setRightTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width //you can use triangleView.frame.size.height
let path = CGMutablePath()
path.move(to: CGPoint(x: heightWidth/2, y: 0))
path.addLine(to: CGPoint(x:heightWidth, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth/2, y:heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setLeftTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: heightWidth/2, y: 0))
path.addLine(to: CGPoint(x:0, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth/2, y:heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setUpTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth, y:heightWidth))
path.addLine(to: CGPoint(x:0, y:heightWidth))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setDownTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x:heightWidth/2, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth, y:0))
path.addLine(to: CGPoint(x:0, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
}

How to add rounded corner to a UIBezierPath custom rectangle?

I managed to create the rounded corners, but I'm having trouble with the first rounded corner (lower right )
Question :
Can I add an (addArcWithCenter) method before the ( moveToPoint ) method ?
How can i get rid of the straight line at the beginning of the rectangle (lower right) ?
here is my code for the custom rectangle and a screenshot :
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 300, y: 0))
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 at the bottom
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.closePath()
I think what you're doing is overly complicated. UIBezierPath gives you UIBezierPath(roundedRect:) so why not use it? Stroke the rounded rectangle; erase the spot where you're going to put the little triangle; add the triangle; fill the compound path; and stroke the missing two sides of the triangle. Like this (this is just some code I happened to have lying around - you should change the numbers to fit your shape, of course):
let con = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(con, 10, 10)
UIColor.blueColor().setStroke()
UIColor.blueColor().colorWithAlphaComponent(0.4).setFill()
let p = UIBezierPath(roundedRect: CGRectMake(0,0,250,180), cornerRadius: 10)
p.stroke()
CGContextClearRect(con, CGRectMake(20,170,10,11))
let pts = [
CGPointMake(20,180), CGPointMake(20,200),
CGPointMake(20,200), CGPointMake(30,180)
]
p.moveToPoint(pts[0])
p.addLineToPoint(pts[1])
p.addLineToPoint(pts[3])
p.fill()
CGContextStrokeLineSegments(con, pts, 4)
A couple of observations:
Make sure that you take the view bounds and inset it by half of the line width. That ensures that the entire stroked border falls within the bounds of the view. If your line width is 1, this might not be so obvious, but with larger line widths, the problem becomes more pronounced.
If using draw(_:) method, don’t use the rect that is passed to this method, but rather refer to the bounds (inset, as described above). The CGRect passed to draw(_:) is the rectangle being drawn, not necessarily the full bounds. (It generally is, but not always, so always refer to the bounds of the view, not the rect passed to this method.)
As the documentation says (emphasis added):
The portion of the view’s bounds that needs to be updated. The first time your view is drawn, this rectangle is typically the entire visible bounds of your view. However, during subsequent drawing operations, the rectangle may specify only part of your view.
I’d give all of the the various properties of the view a didSet observer that will trigger the view to be redrawn. That way, any IB overrides or programmatically set values will be reflected in the resulting view automatically.
If you want, you can make the whole thing #IBDesignable and make the properties #IBInspectable, so you can see this rendered in Interface Builder. It’s not necessary, but can be useful if you want to see this rendered in storyboards or NIBs.
While you can round corners using a circular arc, using a quad curve is easier, IMHO. You just specify where the arc ends and the corner of the rectangle, and the quadratic bezier will produce a nicely rounded corner. Using this technique, no calculation of angles or the center of the arc is necessary.
Thus:
#IBDesignable
public class BubbleView: UIView {
#IBInspectable public var lineWidth: CGFloat = 1 { didSet { setNeedsDisplay() } }
#IBInspectable public var cornerRadius: CGFloat = 10 { didSet { setNeedsDisplay() } }
#IBInspectable public var calloutSize: CGSize = CGSize(width: 10, height: 5) { didSet { setNeedsDisplay() } }
#IBInspectable public var fillColor: UIColor = .yellow { didSet { setNeedsDisplay() } }
#IBInspectable public var strokeColor: UIColor = .black { didSet { setNeedsDisplay() } }
override public func draw(_ rect: CGRect) {
let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)
let path = UIBezierPath()
// lower left corner
path.move(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - calloutSize.height))
path.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.maxY - calloutSize.height - cornerRadius),
controlPoint: CGPoint(x: rect.minX, y: rect.maxY - calloutSize.height))
// left
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
// upper left corner
path.addQuadCurve(to: CGPoint(x: rect.minX + cornerRadius, y: rect.minY),
controlPoint: CGPoint(x: rect.minX, y: rect.minY))
// top
path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
// upper right corner
path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.minY + cornerRadius),
controlPoint: CGPoint(x: rect.maxX, y: rect.minY))
// right
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - calloutSize.height - cornerRadius))
// lower right corner
path.addQuadCurve(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - calloutSize.height),
controlPoint: CGPoint(x: rect.maxX, y: rect.maxY - calloutSize.height))
// bottom (including callout)
path.addLine(to: CGPoint(x: rect.midX + calloutSize.width / 2, y: rect.maxY - calloutSize.height))
path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.midX - calloutSize.width / 2, y: rect.maxY - calloutSize.height))
path.close()
fillColor.setFill()
path.fill()
strokeColor.setStroke()
path.lineWidth = lineWidth
path.stroke()
}
}
That yields:
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()
Swift 5 with configuration variables:
override func draw(_ rect: CGRect) {
let arrowXOffset: CGFloat = 13
let cornerRadius: CGFloat = 6
let arrowHeight: CGFloat = 6
let mainRect = CGRect(origin: rect.origin, size: CGSize(width: rect.width, height: rect.height - arrowHeight))
let leftTopPoint = mainRect.origin
let rightTopPoint = CGPoint(x: mainRect.maxX, y: mainRect.minY)
let rightBottomPoint = CGPoint(x: mainRect.maxX, y: mainRect.maxY)
let leftBottomPoint = CGPoint(x: mainRect.minX, y: mainRect.maxY)
let leftArrowPoint = CGPoint(x: leftBottomPoint.x + arrowXOffset, y: leftBottomPoint.y)
let centerArrowPoint = CGPoint(x: leftArrowPoint.x + arrowHeight, y: leftArrowPoint.y + arrowHeight)
let rightArrowPoint = CGPoint(x: leftArrowPoint.x + 2 * arrowHeight, y: leftArrowPoint.y)
let path = UIBezierPath()
path.addArc(withCenter: CGPoint(x: rightTopPoint.x - cornerRadius, y: rightTopPoint.y + cornerRadius), radius: cornerRadius,
startAngle: CGFloat(3 * Double.pi / 2), endAngle: CGFloat(2 * Double.pi), clockwise: true)
path.addArc(withCenter: CGPoint(x: rightBottomPoint.x - cornerRadius, y: rightBottomPoint.y - cornerRadius), radius: cornerRadius,
startAngle: 0, endAngle: CGFloat(Double.pi / 2), clockwise: true)
path.addLine(to: rightArrowPoint)
path.addLine(to: centerArrowPoint)
path.addLine(to: leftArrowPoint)
path.addArc(withCenter: CGPoint(x: leftBottomPoint.x + cornerRadius, y: leftBottomPoint.y - cornerRadius), radius: cornerRadius,
startAngle: CGFloat(Double.pi / 2), endAngle: CGFloat(Double.pi), clockwise: true)
path.addArc(withCenter: CGPoint(x: leftTopPoint.x + cornerRadius, y: leftTopPoint.y + cornerRadius), radius: cornerRadius,
startAngle: CGFloat(Double.pi), endAngle: CGFloat(3 * Double.pi / 2), clockwise: true)
path.addLine(to: rightTopPoint)
path.close()
}
You can't do this automatically. You have to make the lines shorter and then use arcs of the radius that you want the corner radius to be.
So. Instead of adding a line to x,y you add the line to x-radius, y.
Then add the arc. Then the next line starts at x, y+radius.

Resources