Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


UIBezierPath
addArcWithCenter
addCurveToPoint
addLineToPoint
func createBezierPath() -> UIBezierPath {

    // create a new path
    let path = UIBezierPath()

    // starting point for the path (bottom left)
    path.move(to: CGPoint(x: 2, y: 26))

    // *********************
    // ***** Left side *****
    // *********************

    // segment 1: line
    path.addLine(to: CGPoint(x: 2, y: 15))

    // segment 2: curve
    path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
        controlPoint1: CGPoint(x: 2, y: 14),
        controlPoint2: CGPoint(x: 0, y: 14))

    // segment 3: line
    path.addLine(to: CGPoint(x: 0, y: 2))

    // *********************
    // ****** Top side *****
    // *********************

    // segment 4: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 8, y: 0))

    // segment 6: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 10, y: 12))

    // segment 8: curve
    path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
        controlPoint1: CGPoint(x: 10, y: 14),
        controlPoint2: CGPoint(x: 8, y: 14))

    // segment 9: line
    path.addLine(to: CGPoint(x: 8, y: 26))

    // *********************
    // **** Bottom side ****
    // *********************

    // segment 10: line
    path.close() // draws the final line to close the path

    return path
}
import UIKit
class MyCustomView: UIView {

    override func draw(_ rect: CGRect) {

        // create path (see previous code)
        let path = createBezierPath()

        // fill
        let fillColor = UIColor.white
        fillColor.setFill()

        // stroke
        path.lineWidth = 1.0
        let strokeColor = UIColor.blue
        strokeColor.setStroke()

        // Move the path to a new location
        path.apply(CGAffineTransform(translationX: 10, y: 10))

        // fill and stroke the path (always do these last)
        path.fill()
        path.stroke()

    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}
import UIKit
class MyCustomView: UIView {

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    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.blue.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.position = CGPoint(x: 10, y: 10)

        // add the new layer to our custom view
        self.layer.addSublayer(shapeLayer)
    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
override func viewDidLoad() {
    super.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.yellow
    view.addSubview(myView)

}

Method 1: Draw path in a layer

Method 2: Draw path in draw

  • Bezier Curves (good description of how the mathematical formulas are derived)
  • Bezier Curves (how they are used in graphics applications)
  • Build that path programmatically.
  • Choose a starting point on the path with moveToPoint
  • Close the path with closePath
  • Dark blue numbers are the segments in the order that they will be added programmatically
  • Design the outline of the shape you want.
  • Divide the outline path into segments of lines, arcs, and curves.
  • Dotted lines show the bounding rectangle
  • Draw the path either in drawRect or using a CAShapeLayer.
  • Green dots are the points between path segments
  • Orange dots are the control points for the curves
  • Thinking like a Bzier path (Everything I've ever read from this author is good and the inspiration for my example above came from here.)

@BohdanSavych, the draw method already belongs to the view, so you don't need to add it. It is a standard method defined by UIView for the purpose of drawing. We are just overriding it here so that we can do our own drawing on the view.

@ozgur, There are at least two options. One would be to do a scale (and possibly translate) transform as I showed in my example above. Another option would be to recalculate the Bezier path based on the new frame. In the example above I hard coded all of the numbers into the Bezier path. However, when I have used Bezier paths in actual projects, I determine the Bezier values based on the frame size. When the frame (or more likely, bounds) changes I recalculate the Bezier path.

@ozgur, layoutSubviews definitely sounds like the right place to do it. I would say if that is working then keep it there. Here is an example of where I used a Bezier path. (Scroll to the bottom.) For some reason I didn't put it in layoutSubviews but I don't remember why now. I should probably submit this to code review. I'm no expert. I just made the answer above to learn how to do Bezier paths myself.

And creating our view in the View Controller like this

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

Here is the revised code for our custom view:

Here is what our example design would look like:

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

I chose the latter before. I recalculated the bezier path and updated the layer in question in layoutSubviews method but I am not sure if that is the right place to do it. Where would you update the path when the view's frame has changed?

It wasn't long ago that I didn't even know how to pronounce Bzier, let alone know how to use Bzier paths to make a custom shape. The following is what I have learned. It turns out that they aren't as scary as they seem at first.

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).

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.

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

The basic process is:

These are the main steps:

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

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

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.

What if frame of the view changes? How can we resize the shape when an orientation change occurs?

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

which gives us the same result...

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


All of these are reasonable choices, and which one is best depends on many other factors such as are you going to be continually adding shapes, how often it's called, etc.

There are multiple ways to accomplish what you wish. The ones I've seen most are: override drawRect, draw your shape into a CAShapeLayer and then add it as a sublayer to your view, or draw your path onto another context, save that out as an image, and then add it to your view.

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


Yes , You have to override the drawrect if you want to draw anything.Creating a UIBezierPath can be done anywhere ,But to draw something you have to do it inside the drawrect method

You should be calling setNeedsDisplay if you override drawRect in a subclass of UIView which is basically a custom view drawing something on the screen, like lines,image , rectangle.

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [self createPath].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.position = CGPointMake(100, 100); //etc...
[self.layer addSublayer:shapeLayer];
drawRect

You can use a CAShapeLayer to do this.

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
[[self.view layer] addSublayer:shapeView];
[shapeView setPath:[self createPath].CGPath];
path

It would be easier if you would use a CAShapeLayer, like this:

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


As the other posters pointed out, using a shape layer is a good way to go.

If you want to draw your path yourself then yes, you need to override drawRect for your custom view class.

Shape layers a are likely to give you better performance than overriding drawRect.

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


UIBezierPath
addArcWithCenter
addCurveToPoint
addLineToPoint
func createBezierPath() -> UIBezierPath {

    // create a new path
    let path = UIBezierPath()

    // starting point for the path (bottom left)
    path.move(to: CGPoint(x: 2, y: 26))

    // *********************
    // ***** Left side *****
    // *********************

    // segment 1: line
    path.addLine(to: CGPoint(x: 2, y: 15))

    // segment 2: curve
    path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
        controlPoint1: CGPoint(x: 2, y: 14),
        controlPoint2: CGPoint(x: 0, y: 14))

    // segment 3: line
    path.addLine(to: CGPoint(x: 0, y: 2))

    // *********************
    // ****** Top side *****
    // *********************

    // segment 4: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 8, y: 0))

    // segment 6: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 10, y: 12))

    // segment 8: curve
    path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
        controlPoint1: CGPoint(x: 10, y: 14),
        controlPoint2: CGPoint(x: 8, y: 14))

    // segment 9: line
    path.addLine(to: CGPoint(x: 8, y: 26))

    // *********************
    // **** Bottom side ****
    // *********************

    // segment 10: line
    path.close() // draws the final line to close the path

    return path
}
import UIKit
class MyCustomView: UIView {

    override func draw(_ rect: CGRect) {

        // create path (see previous code)
        let path = createBezierPath()

        // fill
        let fillColor = UIColor.white
        fillColor.setFill()

        // stroke
        path.lineWidth = 1.0
        let strokeColor = UIColor.blue
        strokeColor.setStroke()

        // Move the path to a new location
        path.apply(CGAffineTransform(translationX: 10, y: 10))

        // fill and stroke the path (always do these last)
        path.fill()
        path.stroke()

    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}
import UIKit
class MyCustomView: UIView {

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    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.blue.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.position = CGPoint(x: 10, y: 10)

        // add the new layer to our custom view
        self.layer.addSublayer(shapeLayer)
    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
override func viewDidLoad() {
    super.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.yellow
    view.addSubview(myView)

}

Method 1: Draw path in a layer

Method 2: Draw path in draw

  • Bezier Curves (good description of how the mathematical formulas are derived)
  • Bezier Curves (how they are used in graphics applications)
  • Build that path programmatically.
  • Choose a starting point on the path with moveToPoint
  • Close the path with closePath
  • Dark blue numbers are the segments in the order that they will be added programmatically
  • Design the outline of the shape you want.
  • Divide the outline path into segments of lines, arcs, and curves.
  • Dotted lines show the bounding rectangle
  • Draw the path either in drawRect or using a CAShapeLayer.
  • Green dots are the points between path segments
  • Orange dots are the control points for the curves
  • Thinking like a Bzier path (Everything I've ever read from this author is good and the inspiration for my example above came from here.)

@BohdanSavych, the draw method already belongs to the view, so you don't need to add it. It is a standard method defined by UIView for the purpose of drawing. We are just overriding it here so that we can do our own drawing on the view.

@ozgur, There are at least two options. One would be to do a scale (and possibly translate) transform as I showed in my example above. Another option would be to recalculate the Bezier path based on the new frame. In the example above I hard coded all of the numbers into the Bezier path. However, when I have used Bezier paths in actual projects, I determine the Bezier values based on the frame size. When the frame (or more likely, bounds) changes I recalculate the Bezier path.

@ozgur, layoutSubviews definitely sounds like the right place to do it. I would say if that is working then keep it there. Here is an example of where I used a Bezier path. (Scroll to the bottom.) For some reason I didn't put it in layoutSubviews but I don't remember why now. I should probably submit this to code review. I'm no expert. I just made the answer above to learn how to do Bezier paths myself.

And creating our view in the View Controller like this

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

Here is the revised code for our custom view:

Here is what our example design would look like:

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

I chose the latter before. I recalculated the bezier path and updated the layer in question in layoutSubviews method but I am not sure if that is the right place to do it. Where would you update the path when the view's frame has changed?

It wasn't long ago that I didn't even know how to pronounce Bzier, let alone know how to use Bzier paths to make a custom shape. The following is what I have learned. It turns out that they aren't as scary as they seem at first.

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).

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.

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

The basic process is:

These are the main steps:

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

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

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.

What if frame of the view changes? How can we resize the shape when an orientation change occurs?

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

which gives us the same result...

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
[[self.view layer] addSublayer:shapeView];
[shapeView setPath:[self createPath].CGPath];
path

It would be easier if you would use a CAShapeLayer, like this:

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


Yes , You have to override the drawrect if you want to draw anything.Creating a UIBezierPath can be done anywhere ,But to draw something you have to do it inside the drawrect method

You should be calling setNeedsDisplay if you override drawRect in a subclass of UIView which is basically a custom view drawing something on the screen, like lines,image , rectangle.

Note
Rectangle 27 1

ios Drawing UIBezierPath on code generated UIView?


All of these are reasonable choices, and which one is best depends on many other factors such as are you going to be continually adding shapes, how often it's called, etc.

There are multiple ways to accomplish what you wish. The ones I've seen most are: override drawRect, draw your shape into a CAShapeLayer and then add it as a sublayer to your view, or draw your path onto another context, save that out as an image, and then add it to your view.

Note