How to draw on canvas in IOS, the self-starter way

Dmytro Chaban
DonauTech
Published in
6 min readSep 22, 2019

Suppose you’ve been forced with a task to draw on a canvas with IOS resources. You start looking into resources and everything that you need is so distributed that even blockchain will look like a monolith structure comparing to this. I hope this guide will guide you through everything together. We’ll start with an empty project and go through everything that you need, but if you only stuck on some point just scroll through and find it.

Where should I start working on canvas?

Or, in other words, what is an entry point in the project to draw this first circle/line/whatever. If we look at Android, there’s a pretty method called .onDraw(), that gives you canvas, and so the only thing that you need to do is to create new View and override this method. With IOS, it happens a little bit differently.

We have UIImageView, but we’re not drawing in or on this object. We’re drawing on UIImage, which later on can be inserted into UIImageView and presented to the public. If yo be clear, that’s not the only way to do it. But other ways are too complicated and requires more knowledge of Objective-C, so won’t cover them in this series, at least for now. So, what we need is to create UIIMageView our view controller and that’s the main point on where we’ll start working.

How to create UIImageView to start working on this article?

Skip if you know how to do it. This part is for super-super starters.

  1. Create ImageView in StoryBoard
In new version, you need to click on this square
Find Image View in popup and drag to ViewController
Fill zeros and click Add 4 Constraints

Now you should have single ImageView that spans through whole ViewController.

2. Link ImageView with ViewController class

Click on Show the Assistant editor
Click on UIImageVIew with control and drag to the right screen
Fill in the name and click Connect

Now you have imageView connected to the ViewController and you can start working on our own drawing framework. And the first thing that you need to do is to initiate drawing session.

How to initiate drawing session?

To start the drawing session, we have a method called UIGraphicsBeginImageContext(CGSize size) . That’s where the fun begins. You can imagine that IOS has singleton called DrawingSession, and when you call this method, it initiates some array that corresponds to CGSize. Specifically, it creates CGContext that you need to obtain, to do it, just call

UIGraphicsBeginImageContext(CGSize.init(width: drawImageView.bounds.width, height: drawImageView.bounds.height))
let c = UIGraphicsGetCurrentContext()!

Variable c, in this case, is just a convenient way to name context. You’ll call context several times. Now you’re ready to draw. Depending on where you want this drawing to occur, you can put it in e.g viewDidLoad or touchesBegan. We’ll cover touchesBegan in next article. Now let’s do some drawing.

How to draw with context?

Now that we have GraphicsContext, we can do some actual drawings. I’d like to give you exactly what you want, but I’m not a telepath, so, I’ll try to cover as much as possible. If you’re looking for user interactions with a canvas that’ll be in the next article. Let’s start with the coordinates system.

How does coordinate system work?

Let’s look at the image below. It’s not correct to say that DrawingContext exists on the screen, but it’s an easy way to imagine how it works so that you can operate it correctly.

We see that the coordinate system starts from the top left corner. You can say that X is how far away to draw from the left part of a screen. And Y is how far away from the top of a screen, or “how deep”. We also have bounds of our image that we need to keep in mind. It won’t hurt if the width of your image is 100px and you’ll draw the line with width 1000px. But you won't see anything above 100px unless you apply a larger image.

Keep in mind that you don’t always have UIImageView on the whole screen. It can be a small area 16x16 px. But these rules will still apply. Except zero coordinates will start from top left image corner wherever this image is.

How to draw Path?

Now that we know how to navigate through the coordinate system, we can draw some Path. The path is a sequence of positions connected together and can have fill and stroke.

It’s easier to divide this process into two phases: construction and drawing. Construction is where you plot the shape of Path and drawing is where you directly draw this Path on the image, applying colors, etc.

In order to start constructing Path:

c.beginPath()
c.move(to: CGPoint.init(x: 0, y:0)) //use position that you want

The first thing is just c.beginPath() to start the path process. If you’ll type this command once again all the process that you’ve done will be gone. Once we began the path, we need to set coordinates from which we want to draw e.g line.

We can, for example, draw a few lines:

c.addLine(to: CGPoint.init(x: 100, y: 100))
c.addLine(to: CGPoint.init(x: 100, y: 0))

What we actually do with this command is we say to IOS: Plot a line from our last used or initial position(which we set with c.move(to:)) to a point with x and y.

When you are done constructing a Path , it’s time to start drawing phase. First thing is that we need to close our path:

c.closePath()

First thing is that it’ll draw the last line that will connect the first point of your image with the last one so that Path will be closed.

Also, it will block any later construction commands on current Path that you’ll execute (e.g addLine() ). what you can do is to execute move() once again and continue drawing.

Next step is to define which color will our Path be. As I said before, we have Stroke and Fill(last image above). Stroke is color that’ll be used to draw (surprise) contour for our Path. Fill, of course, is responsible for the inside part of Path. It’s done really easy:

c.setFillColor(UIColor.blue.cgColor)c.setStrokeColor(UIColor.green.cgColor)

We need to supply CGColor, fastest way is to use UIColor and take the .cgColor from it.

And, finally, last action that is required is to finally draw the path:

c.drawPath(using: CGPathDrawingMode.fillStroke)

This is where we need to specify how you want to draw the path, there are few options: fill , stroke and fillStroke . Correspondingly, the first one will draw only fill, the second one only stroke and the third one both.

How to finish drawing session?

Suppose you’re finished drawing everything, now it’s time to move everything on a real screen. This is done with almost one command:

var image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
drawImageView.image = image

On the first line, we take the image from our drawing context. We also end our image context because we don’t want to draw anything more. And the last thing is that we set our new image to ImageView. Why I save the image into a variable is that we’ll use it in the second part of this article. Here’s the whole code(in viewDidLoad() ):

UIGraphicsBeginImageContext(CGSize.init(width: drawImageView.bounds.width, height: drawImageView.bounds.height))
let c = UIGraphicsGetCurrentContext()!
c.beginPath()c.move(to: CGPoint.init(x: 0, y:0))c.addLine(to: CGPoint.init(x: 100, y: 100))c.addLine(to: CGPoint.init(x: 100, y: 0))c.closePath()c.setFillColor(UIColor.blue.cgColor)c.setStrokeColor(UIColor.green.cgColor)c.drawPath(using: CGPathDrawingMode.eoFill)let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()drawImageView.image = image

When you run this code you can see next:

And that’s a good sign that we’ve done everything right.

On the next article we’ll have a look at what we can draw with our context. Thank you for reading this article :)

--

--

Dmytro Chaban
DonauTech

Software Engineer, addicted to productivity and automatization