Drawing with UIKit's UIBezierPath

This article’s banner, written in the center: “UIKit — UIBezierPath — Bézier Curves”
Part 2/5

Introduction

  1. Workplace (setting up the project)
  2. UIBezierPath initializers (what are they and what do they do)
  3. Drawing a simple shape (to understand what we need to do besides draw)
  4. Drawing our first curved shape
  5. Drawing this article banner!

Workplace

  • Changed the AccentColor to a custom red (#BE0606);
  • Deleted the ViewController.swift file and everything inside the main.storyboard file;
  • Added a Tab Bar Controller and connected 3 view controllers to it;
  • Created 3 new view controllers (SquareViewController, DShapeViewController, and BannerViewController), connected them to the storyboard screens and added labels to them (to differentiate the screens).
The app views' workflow, consisting of a tab bar with 3 views
App workflow

UIBezierPath initializers

A print of the Xcode class initialization code for the UIBezierPath, consisting of various initializations
UIBezierPath initializers
  1. UIBezierPath()
    A normal UIBezierPath class, is empty and you can make everything from scratch — you'll probably end up using this;
  2. UIBezierPath(arcCenter:radius:startAngle:endAngle:clockwise:)
    A circle (which you can set his center, size, where it starts and ends, and if it is clockwise or not — this can be important for animations);
  3. UiBezierPath(ovalIn:)
    You can create an oval shape with this method;
  4. UiBezierPath(rect:)
    This one lets you create a rectangle;
  5. UiBezierPath(roundedRect:cornerRadii)
    A rectangle with rounded corners (passing the size of the rectangle and the corner radius);
  6. UiBezierPath(roundedRect:byRoundingCorners:cornerRadii)
    And this one you can set exactly which corners you want to be rounded.

Drawing a simple shape

A simple square colored in red
Simple red square
  • Creating an attribute that will store our object, and
  • Calling the method that will set up our drawing in the viewDidLayoutSubviews method:
class SquareViewController: UIViewController {
var shape: CAShapeLayer?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
shape?.removeFromSuperlayer()
drawSquare()
}
}
  • Create and configure the CAShapeLayer
  • Create and draw with UIBezierPath
  • Pass the bezier path to the shape layer and add the layer to the view hierarchy
func drawSquare() {
let shapeLayer = CAShapeLayer()
shapeLayer.position = self.view.center
shapeLayer.fillColor = UIColor.tintColor.cgColor
}
A graph showcasing which positions each vertex of a square will be for the current example.
func drawSquare() {
/// ...
let bezierPath = UIBezierPath()
bezierPath.move(to: .zero)
bezierPath.addLine(to: CGPoint(x: 100, y: 0))
bezierPath.addLine(to: CGPoint(x: 100, y: 100))
bezierPath.addLine(to: CGPoint(x: 0, y: 100))
bezierPath.addLine(to: .zero)
bezierPath.close()
}
func drawSquare() {
/// ...
shapeLayer.path = bezierPath.cgPath
self.view.layer.addSublayer(shapeLayer)
self.shape = shapeLayer
}
A print of an iPhone simulator showing the code running
Simulator with the square shape

Drawing our first curved shape

A simple D shape, with red borders
Simple “D” shape with a red stroke
Curve example and its control points
Different curves with different control points positions for demonstration purposes
Examples of different control point positions
func drawDShape() {
let shapeLayer = CAShapeLayer()
shapeLayer.position = self.view.center
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 5
shapeLayer.strokeColor = drawingColor

// Start the bezier path object
let bezierPath = UIBezierPath()

// In this comment we will draw our custom shape
shapeLayer.path = bezierPath.cgPath
self.view.layer.addSublayer(shapeLayer)
self.shape = shapeLayer
}
An image of a simple graph showcasing where each point and control point should be in the code
Curve point positions
  • Move to the first point;
  • Add a curve to the second point;
  • Define our curve control points (top right and bottom right)
  • Add a line back to the first point
func drawDShape() {
bezierPath.move(to: .zero)
bezierPath.addCurve(
to: CGPoint(x: 0, y: 100),
controlPoint1: CGPoint(x: 75, y: 0),
controlPoint2: CGPoint(x: 75, y: 100))
bezierPath.addLine(to: .zero)
}
A print of an iPhone simulator showing the code running
Simulator with the “D” shape

Drawing this article banner

The article banner design which consists of 2 custom shapes, one at the top and the other at the bottom
This article banner
class BannerViewController: UIViewController {
var topShapeLayer: CAShapeLayer?
var botShapeLayer: CAShapeLayer?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
topShapeLayer?.removeFromSuperlayer()
botShapeLayer?.removeFromSuperlayer()
addTopDrawing()
addBotDrawing()
}
}
The banner and its lines, curves, points, and control points
  1. Move to the 1st (I'll use the top left);
  2. Add a line to the 2nd (top right);
  3. Add a line to the 3rd (center right);
  4. Add a big curve to the 4th point (top left);
  5. Add a small curve to the 5th point (also at the top left)
  6. Add a line back to the 1st point.
func addTopDrawing () {
// ...
let rect = UIScreen.main.bounds
path.move(to: .zero)
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height*0.3125))
path.addCurve(to: CGPoint(x: 40, y: 75),
controlPoint1: CGPoint(
x: rect.width-40,
y: rect.height*0.15625),
controlPoint2: CGPoint(
x: rect.width*3/5,
y: rect.height*0.09375))
path.addCurve(to: CGPoint(x: 0, y: 50),
controlPoint1: CGPoint(x: 10, y: 75),
controlPoint2: CGPoint(x: 0, y: 65))
path.addLine(to: .zero)

path.close()
// ...
}
func addBotDrawing () {
// ...
let rect = UIScreen.main.bounds
let width = rect.width
let height = rect.height
path.move(to: CGPoint(x: 0, y: rect.height*0.6875)) path.addCurve(to: CGPoint(x: width-40, y: height-75),
controlPoint1: CGPoint(
x: 40,
y: height*0.84375),
controlPoint2: CGPoint(
x: width*2/5,
y: height*0.90625))

path.addCurve(to: CGPoint(x: width, y: height-50),
controlPoint1: CGPoint(
x: width-10,
y: height-75),
controlPoint2: CGPoint(
x: width,
y: height-65))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: 0, y: rect.height*0.6875))
path.close()
// ...
}

Conclusion

--

--

Software Engineer, iOS developer (native and Flutter). I also enjoy learning about design, security, code smells and machine learning. He/him

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Luiz Pedro Franciscatto Guerra

Software Engineer, iOS developer (native and Flutter). I also enjoy learning about design, security, code smells and machine learning. He/him