UIKit Drawing Tutorial: How to Make a Simple Drawing App

Learn how to create a simple drawing app in Swift using UIKit drawing APIs. By Ron Kliffer.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Adding Colors

It’s time to add a splash of color to the scene — line art alone is kind of drab.

There are 10 color buttons on the screen at the moment, but if you tap any button right now, nothing happens. To get this working, first you’ll need to define all the colors.

Open Pencil.swift. Pencil is an enum that represents the various color options the user can choose from. It has a single initializer init?(tag:) that accepts a button tag and turns it into the desired pencil. Add the following computed property to Pencil:

var color: UIColor {
  switch self {
  case .black:
    return .black
  case .grey:
    return UIColor(white: 105/255.0, alpha: 1.0)
  case .red:
    return UIColor(red: 1, green: 0, blue: 0, alpha: 1.0)
  case .darkblue:
    return UIColor(red: 0, green: 0, blue: 1, alpha: 1.0)
  case .lightBlue:
    return UIColor(red: 51/255.0, green: 204/255.0, blue: 1, alpha: 1.0)
  case .darkGreen:
    return UIColor(red: 102/255.0, green: 204/255.0, blue: 0, alpha: 1.0)
  case .lightGreen:
    return UIColor(red: 102/255.0, green: 1, blue: 0, alpha: 1.0)
  case .brown:
    return UIColor(red: 160/255.0, green: 82/255.0, blue: 45/255.0, alpha: 1.0)
  case .orange:
    return UIColor(red: 1, green: 102/255.0, blue: 0, alpha: 1.0)
  case .yellow:
    return UIColor(red: 1, green: 1, blue: 0, alpha: 1.0)
  case .eraser:
    return .white
  }
}

This returns the appropriate UIColor for each selected pencil.

Next, open ViewController.swift, add the following to pencilPressed(_:):

// 1
guard let pencil = Pencil(tag: sender.tag) else {
  return
}

// 2
color = pencil.color

// 3
if pencil == .eraser {
  opacity = 1.0
}

Here’s a look at this, step by step:

  1. First, you need to know which color index the user selected. There are many places this could go wrong, such as using an incorrect tag or a tag not set, so you guard against there not being a matching Pencil.
  2. Next, you set the drawing color to the color the user selected.
  3. The last color is the eraser, so it’s a bit special. The eraser button sets the color to white and opacity to 1.0. As your background color is also white, this will give you a very handy eraser effect!

What? Time for more drawing already? Yup — build and run, and get ready to let the colors fly. Now, tapping a color button changes the brush stroke to use that button’s color. No more drab line art!

Resetting to Tabula Rasa

All great artists have those moments in which they step back and shake their heads muttering, “No! No! This will never do!” You’ll want to provide a way to clear the drawing canvas and start over again. You already have a Reset button set up in your app for this.

In ViewController.swift, add the following to resetPressed(_:):

mainImageView.image = nil

That’s it, believe it or not! All the code above does is set the mainImageView‘s image to nil, and voila! Your canvas is cleared! Remember, you drew lines into the image view’s image context, so clearing that out to nil here will reset everything.

Build and run your code again. Draw something, and then tap the Reset button to clear your drawing. There! No need to go tearing up canvases in frustration.

Adding Finishing Touches — Settings

You now have a functional drawing app, but there’s still that second screen of Settings to deal with.

Open Main.storyboard and click on the Settings View Controller scene. The Settings scene has five UISlider components to set the brush width, brush opacity and brush RGB color values. You can also see a UIImageView that shows a preview generated from all the selected values.

Now, open SettingsViewController.swift and add the following properties to the class:

var brush: CGFloat = 10.0
var opacity: CGFloat = 1.0
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0

This will let you keep track of the brush size, opacity and color the user selects.

Next, add the following method:

func drawPreview() {
  UIGraphicsBeginImageContext(previewImageView.frame.size)
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
    
  context.setLineCap(.round)
  context.setLineWidth(brush)
  context.setStrokeColor(UIColor(red: red, 
                                 green: green,
                                 blue: blue, 
                                 alpha: opacity).cgColor)
  context.move(to: CGPoint(x: 45, y: 45))
  context.addLine(to: CGPoint(x: 45, y: 45))
  context.strokePath()
  previewImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
}

This method uses the same techniques to draw a preview of the settings that drawLine(from:to:) used in ViewController. However, in this case, the method draws a single point rather than a line with the appropriate line width and opacity from the slider values.

Next, add the following to brushChanged(_:):

brush = CGFloat(sender.value)
labelBrush.text = String(format: "%.1f", brush)
drawPreview()

and add the following to opacityChanged(_:):

opacity = CGFloat(sender.value)
labelOpacity.text = String(format: "%.1f", opacity)
drawPreview()

In the code above, as the user changes the slider values, you need to set the respective values accordingly and then update the preview image by calling drawPreview().

Build and run, open the Settings screen, and play around with the sliders. You’ll see that now the preview image and value labels change as you move them! The color sliders don’t work yet, but you’ll come back to them shortly.

Integrating Settings

There’s still one important piece missing here. Did you notice what it was?

The updated opacity and width values are still not being applied to the ViewController drawing canvas! That’s because you have not yet communicated the values specified in the Settings screen to the ViewController yet. This is a perfect job for a delegate protocol.

Open SettingsViewController.swift and add the following code just below the import:

protocol SettingsViewControllerDelegate: class {
  func settingsViewControllerFinished(_ settingsViewController: SettingsViewController)
}

This defines a class protocol with one required method. The settings screen will use this communicate back with any party interested in what the updated settings.

Also add a property to SettingsViewController:

weak var delegate: SettingsViewControllerDelegate?

This will hold the reference to the delegate. If there is a delegate, you’ll need to notify it when the user taps the Close button. Add the following to closePressed(_:):

delegate?.settingsViewControllerFinished(self)

This calls the delegate method so it can update the delegate with the new values.

Now, open ViewController.swift and add a new class extension for the protocol to the bottom of the file:

// MARK: - SettingsViewControllerDelegate

extension ViewController: SettingsViewControllerDelegate {
  func settingsViewControllerFinished(_ settingsViewController: SettingsViewController) {
    brushWidth = settingsViewController.brush
    opacity = settingsViewController.opacity
    dismiss(animated: true)
  }
}

This declares the class as conforming to SettingsViewControllerDelegate and implements its single method. In the implementation, all it needs to do is set the current brush width and opacity to the values from the settings view’s slider controls.

When the user moves from the Drawing to the Settings screen, you’ll want the sliders to show the currently selected values for brush size and opacity. That means you’ll need to pass them along when you open Settings.

Add the following method override to the ViewController class:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  guard 
    let navController = segue.destination as? UINavigationController,
    let settingsController = navController.topViewController as? SettingsViewController 
  else {
      return
  }
  settingsController.delegate = self
  settingsController.brush = brushWidth
  settingsController.opacity = opacity
}

When the user triggers the segue by tapping the Settings button, this method configures the new SettingsViewController by setting itself as the delegate and passing the current brush and opacity values through.

Build and run. At this stage, you’ll see the brush and opacity values are now updated after you change them in the settings screen. Now you can draw with many colors of different brush sizes and opacity levels!