How To Make A Simple Drawing App with UIKit and Swift

Learn how to make your own drawing app, including different colors and brushes, using UIKit and Core Graphics in this tutorial! By Jean-Pierre Distler.

Leave a rating/review
Save for later
Share

How To Make A Simple Drawing App with UIKit

How To Make A Simple Drawing App with UIKit

Update 4/10/2015 Updated for Xcode 6.3 and Swift 1.2

Update note: This tutorial was updated to iOS 8 and Swift by Jean-Pierre Distler. Original post by tutorial team member Abdul Azeem Khan.

At some stage in our lives, we all enjoyed drawing pictures, cartoons, and other stuff.

For me it was using a pen and paper when I was growing up, but these days the old pen and paper has been replaced by the computer and mobile devices! Drawing can be especially fun on touch-based devices, as you can see by the abundance of drawing apps on the App Store.

Want to learn how to make a drawing app of your own? The good news is it’s pretty easy, thanks to some great drawing APIs available in iOS.

In this tutorial, you will create an app very much like Color Pad for iPhone. In the process you’ll learn how to:

  • draw lines and strokes using Quartz2D,
  • use multiple colors,
  • set brush stroke widths and opacity,
  • create an eraser,
  • create a custom RGB color selector, and
  • share your drawing!

Grab your pencils and get started; no need to make this introduction too drawn out! :]

Getting Started

Start by downloading the starter project.

Start Xcode, open the project and have a look on the files inside. As you can see I have not done too much work for you. I added all needed images to the asset catalog and created the main view of the app with all needed constraints. The whole project is based on the Single View Application template.

Now open Main.storyboard and have look at the interface. The View Controller Scene has three buttons on the top. As the titles say they will be used to reset or share a drawing and to bring up a settings screen. On the bottom you can see more buttons with pen images and an eraser. They will be used to select colors.

Finally there are two image views called mainImageView and tempImageView – you’ll see later why you need two image views when you use them to allow users to draw with different brush opacity levels.

MainStoryboard2

The view controller has the actions and outlets set as you’d expect: each button at the top has an action, the pencil colors all link to the same action (they have different tags set to tell them apart), and there are outlets for the two image views.

In order to let your inner artist shine, you’ll need to add some code!

Quick on the Draw

Your app will start off with a simple Drawing Feature, whereby you can swipe your finger on the screen to draw simple black lines. (Hey, even Picasso started with the basics).

Open ViewController.swift and add the following properties to the class:

var lastPoint = CGPoint.zeroPoint
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false

Here’s a quick explanation of the variables used above:

  • lastPoint stores the last drawn point on the canvas. This is used when a continuous brush stroke is being drawn on the canvas.
  • red, green, and blue store the current RGB values of the selected color.
  • brushWidth and opacity store the brush stroke width and opacity.
  • swiped identifies if the brush stroke is continuous.

The default RGB values are all 0, which means the drawing color will start out as black just for now. The default opacity is set to 1.0 and line width is set to 10.0.

Now for the drawing part! All touch-notifying methods come from the parent class UIResponder; they are fired in response to touches began, moved, and ended events. You’ll use these three methods to implement your drawing logic.

Start by adding the following method:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
  swiped = false
  if let touch = touches.first as? UITouch {
    lastPoint = touch.locationInView(self.view)
  }
}

touchesBegan is called when the user puts a finger down on the screen. This is the start of a drawing event, so you first reset swiped to false since the touch hasn’t moved yet. You also save the touch location in lastPoint so when the user starts drawing with their finger, you can keep track of where they started. This is, so to speak, where the brush hits the paper! :]

Add the following two methods next:

func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
  
  // 1
  UIGraphicsBeginImageContext(view.frame.size)
  let context = UIGraphicsGetCurrentContext()
  tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
  
  // 2
  CGContextMoveToPoint(context, fromPoint.x, fromPoint.y)
  CGContextAddLineToPoint(context, toPoint.x, toPoint.y)
  
  // 3
  CGContextSetLineCap(context, kCGLineCapRound)
  CGContextSetLineWidth(context, brushWidth)
  CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)
  CGContextSetBlendMode(context, kCGBlendModeNormal)
  
  // 4
  CGContextStrokePath(context)
  
  // 5
  tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  tempImageView.alpha = opacity
  UIGraphicsEndImageContext()
  
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
  // 6
  swiped = true
  if let touch = touches.first as? UITouch {
    let currentPoint = touch.locationInView(view)
    drawLineFrom(lastPoint, toPoint: currentPoint)
    
    // 7
    lastPoint = currentPoint
  }
}

Here’s what’s going on in this method:

  1. The first method is responsible for drawing a line between two points. Remember that this app has two image views – mainImageView (which holds the “drawing so far”) and tempImageView (which holds the “line you’re currently drawing”). Here you want to draw into tempImageView, so you need to set up a drawing context with the image currently in the tempImageView (which should be empty the first time).
  2. Next, you get the current touch point and then draw a line with CGContextAddLineToPoint from lastPoint to currentPoint. You might think that this approach will produce a series of straight lines and the result will look like a set of jagged lines. This will produce straight lines, but the touch events fire so quickly that the lines are short enough and the result will look like a nice smooth curve.
  3. Here are all the drawing parameters for brush size and opacity and brush stroke color.
  4. This is where the magic happens, and where you actually draw the path!
  5. Next, you need to wrap up the drawing context to render the new line into the temporary image view.
  6. In touchesMoved, you set swiped to true so you can keep track of whether there is a current swipe in progress. Since this is touchesMoved, the answer is yes, there is a swipe in progress! You then call the helper method you just wrote to draw the line.
  7. Finally, you update the lastPoint so the next touch event will continue where you just left off.

Next, add the final touch handler:

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {

  if !swiped {
    // draw a single point
    drawLineFrom(lastPoint, toPoint: lastPoint)
  }
  
  // Merge tempImageView into mainImageView
  UIGraphicsBeginImageContext(mainImageView.frame.size)
  mainImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: 1.0)
  tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: opacity)
  mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
  
  tempImageView.image = nil
}

First, you check if the user is in the middle of a swipe. If not, then it means the user just tapped the screen to draw a single point. In that case, just draw a single point using the helper method you wrote earlier.

If the user was in the middle of a swipe then that means you can skip drawing that single point – since touchesMoved was called before, you don’t need to draw any further since this is touchesEnded.

The final step is to merge the tempImageView with mainImageView. You drew the brush stroke on tempImageView rather than on mainImageView. What’s the point of an extra UIImageView — can’t you just draw directly to mainImageView?

You could, but the dual image views are used to preserve opacity. When you’re drawing on tempImageView, the opacity is set to 1.0 (fully opaque). However, when you merge tempImageView with mainImageView, you can set the tempImageView opacity to the configured value, thus giving the brush stroke the opacity you want. If you were to draw directly on mainImageView, it would be incredibly difficult to draw brush strokes with different opacity values.

Okay, time to get drawing! Build and run your app. You will see that you can now draw pretty black lines on your canvas!

SecondRun

That’s a great start! With just those touch handling methods you have a huge amount of the functionality in place. Now it’s time to fill in some more options, starting with color.