Designing and drawing a Bezier Path

suggest change

This example shows the process from designing the shape you want to drawing it on a view. A specific shap is used but the concepts you learn can be applied to any shape.

How to draw a Bézier path in a custom view

These are the main steps:

  1. Design the outline of the shape you want.
  2. Divide the outline path into segments of lines, arcs, and curves.
  3. Build that path programmatically.
  4. Draw the path either in drawRect or using a CAShapeLayer.

Design shape outline

You could do anything, but as an example I have chosen the shape below. It could be a popup key on a keyboard.

Divide the path into segments

Look back at your shape design and break it down into simpler elements of lines (for straight lines), arcs (for circles and round corners), and curves (for anything else).

Here is what our example design would look like:

Build the path programmatically

We’ll arbitrarily start in the bottom left corner and work clockwise. I’ll use the grid in the image to get the x and y values for the points. I’ll hardcode everything here, but of course you wouldn’t do that in a real project.

The basic process is:

  1. Create a new UIBezierPath
  2. Choose a starting point on the path with moveToPoint
  3. Add segments to the path
  1. Close the path with closePath

Here is the code to make the path in the image above.

func createBezierPath() -> UIBezierPath {
    // create a new path
    let path = UIBezierPath()
    // starting point for the path (bottom left)
    path.moveToPoint(CGPoint(x: 2, y: 26))
    // *********************
    // ***** Left side *****
    // *********************
    // segment 1: line
    path.addLineToPoint(CGPoint(x: 2, y: 15))
    // segment 2: curve
    path.addCurveToPoint(CGPoint(x: 0, y: 12), // ending point
        controlPoint1: CGPoint(x: 2, y: 14),
        controlPoint2: CGPoint(x: 0, y: 14))
    // segment 3: line
    path.addLineToPoint(CGPoint(x: 0, y: 2))
    // *********************
    // ****** Top side *****
    // *********************
    // segment 4: arc
    path.addArcWithCenter(CGPoint(x: 2, y: 2), // center point of circle
        radius: 2, // this will make it meet our path line
        startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
        endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
        clockwise: true) // startAngle to endAngle goes in a clockwise direction
    // segment 5: line
    path.addLineToPoint(CGPoint(x: 8, y: 0))
    // segment 6: arc
    path.addArcWithCenter(CGPoint(x: 8, y: 2),
        radius: 2,
        startAngle: CGFloat(3*M_PI_2), // straight up
        endAngle: CGFloat(0), // 0 radians = straight right
        clockwise: true)
    // *********************
    // ***** Right side ****
    // *********************
    // segment 7: line
    path.addLineToPoint(CGPoint(x: 10, y: 12))
    // segment 8: curve
    path.addCurveToPoint(CGPoint(x: 8, y: 15), // ending point
        controlPoint1: CGPoint(x: 10, y: 14),
        controlPoint2: CGPoint(x: 8, y: 14))
    // segment 9: line
    path.addLineToPoint(CGPoint(x: 8, y: 26))
    // *********************
    // **** Bottom side ****
    // *********************
    // segment 10: line
    path.closePath() // draws the final line to close the path
    return path

Note: Some of the above code can be reduced by adding a line and an arc in a single command (since the arc has an implied starting point). See here for more details.

Draw the path

We can draw the path either in a layer or in drawRect.

Method 1: Draw path in a layer

Our custom class looks like this. We add our Bezier path to a new CAShapeLayer when the view is initialized.

import UIKit
class MyCustomView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    func setup() {
        // Create a CAShapeLayer
        let shapeLayer = CAShapeLayer()
        // The Bezier path that we made needs to be converted to 
        // a CGPath before it can be used on a layer.
        shapeLayer.path = createBezierPath().CGPath
        // apply other properties related to the path
        shapeLayer.strokeColor = UIColor.blueColor().CGColor
        shapeLayer.fillColor = UIColor.whiteColor().CGColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.position = CGPoint(x: 10, y: 10)
        // add the new layer to our custom view

    func createBezierPath() -> UIBezierPath {
        // see previous code for creating the Bezier path

And creating our view in the View Controller like this

override func viewDidLoad() {
    // create a new UIView and add it to the view controller
    let myView = MyCustomView()
    myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
    myView.backgroundColor = UIColor.yellowColor()

We get…

Hmm, that’s a little small because I hardcoded all the numbers in. I can scale the path size up, though, like this:

let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
shapeLayer.path = path.CGPath

Method 2: Draw path in drawRect

Using drawRect is slower than drawing to the layer, so this is not the recommended method if you don’t need it.

Here is the revised code for our custom view:

import UIKit
class MyCustomView: UIView {
    override func drawRect(rect: CGRect) {
        // create path (see previous code)
        let path = createBezierPath()
        // fill
        let fillColor = UIColor.whiteColor()
        // stroke
        path.lineWidth = 1.0
        let strokeColor = UIColor.blueColor()
        // Move the path to a new location
        path.applyTransform(CGAffineTransformMakeTranslation(10, 10))
        // fill and stroke the path (always do these last)
    func createBezierPath() -> UIBezierPath {
        // see previous code for creating the Bezier path

which gives us the same result…

Further study

Excellent articles for understanding Bezier paths.


Feedback about page:

Optional: your email if you want me to get back to you:

Table Of Contents