How to make a simple 2D drawing app in Swift

UIKit has powerful tools and API that enable 2D drawing, which comes in handy often when we need to draw fast hand sketches and drawings.

Many apps in Apple store are built to empower finger drawing, and your app can start to become the next one 🙂

In this tutorial, I gonna show you the simple techniques behind 2D drawing and how to implement them to make your own drawing app.

At the end of this tutorial, you will have a neat board in which you can draw your custom sketches and save them to the Photo Library as images.

First, download the starter project here.

Open up the project and select Main.storyboard file from the Project navigator view. The screen is composed of two buttons located in the navigation bar, the left button will erase the drawing and reinitialise a clean board, while the right button will save the drawing to the Photo Library of your device. The rest of the screen is a white board in which you gonna draw your sketches.

Cool, now place to the code. Select ViewController.swift from the Project navigator and take a look at the code already put in there. You may notice that I have already hook up two action methods and a UIImageView object to the code. The ‘saveImage’ method is bound to the right button and the ‘undoDrawing’ method is bound to the left button, while the ‘imageView’ property is actually a reference for the white board you gonna draw in.

Copy the following vars declarations just under the ViewController class name:
a

var lastPoint:CGPoint!
var isSwiping:Bool!
var red:CGFloat!
var green:CGFloat!
var blue:CGFloat!

The ‘lastPoint’ var will store the coordinate (x,y) of the touch in the screen, so that UIKit knows from where it should start and where it should stop drawing. You also declared a bool variable, this is a flag to recognise whether you are swiping your finger or it’s just a one-touch on the screen. The rest of the variables are float values for the drawing main color.

Before you go further, let’s initialise the color code vars. Copy the following code inside the viewDidLoad method:

 red   = (0.0/255.0)
 green = (0.0/255.0)
 blue  = (0.0/255.0)

The generated code above is black, feel free to change it to whatever color you feel confortable with.

Now, we need to implement some touches handler methods in order to respond to finger gestures and draw properly.

Start by placing the following code before the closing bracket of the class:

override func touchesBegan(touches: Set,
     withEvent event: UIEvent?){
      isSwiping    = false
      if let touch = touches.first{
         lastPoint = touch.locationInView(imageView)
      }
}

The code above will run as soon as you touch the screen, it will set the flag ‘isSwiping’ to false assuming you are just typing a single dot in the screen. Also, it will store the last touched point coordinate in the board.

Next, copy the following method:

  override func touchesMoved(touches: Set,
        withEvent event: UIEvent?){
            
            isSwiping = true;
            if let touch = touches.first{
                let currentPoint = touch.locationInView(imageView)
                UIGraphicsBeginImageContext(self.imageView.frame.size)
                self.imageView.image?.drawInRect(CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height))
                CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
                CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y)
                CGContextSetLineCap(UIGraphicsGetCurrentContext(),kCGLineCapRound)
                CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 9.0)
                CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),red, green, blue, 1.0)
                CGContextStrokePath(UIGraphicsGetCurrentContext())
                self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                lastPoint = currentPoint
            }
    }

The method above will start running as soon as you hold your finger and move it around, and will run continuously as long as your finger is moving. This is the right place to start drawing lines. First, you set the flag ‘isSwiping’ to true so that the app will recognise it should draw continuous dots instead of a single point. Then the code will continuously catch the current point as you move and draw a line (continuous dots) to reach it in the screen. This way, you will notice UIKit is following your finger movement with a black line as you swipe around in the board.

Lastly, we need to notify the controller each time you release your finger off the screen. This is the right place to detect a single dot touch and draw it. To do so, copy the following code below:

    override func touchesEnded(touches: Set,
        withEvent event: UIEvent?){
            if(!isSwiping) {
               UIGraphicsBeginImageContext(self.imageView.frame.size)
                self.imageView.image?.drawInRect(CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height))
                CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound)
                CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 9.0)
                CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0)
                CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
                CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
                CGContextStrokePath(UIGraphicsGetCurrentContext())
                self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
            }
}

As you may notice, the code above is similar to the code in the ‘touchesMoved’ method, except that it will run after you release the mouse/finger touch, and also after checking whether the ‘isSwiping’ flag is set to false (which means there was a single touch on the screen).

So far so good. Build and run the project and try to play around with your finger or mouse pointer, if you run on the simulator.

Drawing with UIKit

To finish up with your drawing app, let’s implement the two action methods bound to the left and right buttons in the navigation bar. Locate the ‘undoDrawing’ method and place the following statement inside:

self.imageView.image = nil

This will reinitialise the imageView’s image property by setting it to nil and hence clearing the board for new drawings.

Next, place the following code inside the ‘saveImage’ method:

if self.imageView.image == nil{
   return
}
UIImageWriteToSavedPhotosAlbum(self.imageView.image!,self, Selector("image:withPotentialError:contextInfo:"), nil)

UIImageWriteToSavedPhotosAlbum is a UIKit API that save an image to the camera roll album. You just pass the image object and a selector method to call after the saving operation is completed. Copy the following code before the closing bracket of the class:

func image(image: UIImage, withPotentialError error: NSErrorPointer, contextInfo: UnsafePointer<()>) {
        UIAlertView(title: nil, message: "Image successfully saved to Photos library", delegate: nil, cancelButtonTitle: "Dismiss").show()
}

That’s it, build and run the app. Draw, erase, draw again and save to the Photo Library. Well, enjoy your work 🙂

As usual, you can download the completed project here.

You can enhance the project by implementing a color palette, automatic shapes drawings, etc. Let me know your thoughts in the comments below, I’d love to hear from you!

Malek
iOS developer with over than 11 years of extensive experience working on several projects with different sized startups and corporates.

13 Comments

  1. Hi timbojill,

    You can easily disable user interaction on the drawing board by setting its “userInteractionEnabled” property to false, or even set the “hidden” property to false. Add a new button in storyboard, and attach to it an action method in which you set one of those properties. More info about UIView properties :

    https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/instp/UIView/hidden

    https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/instp/UIView/userInteractionEnabled

  2. Just wondering if you plan on adding extra functionality. Like allowing the user to select the color of the pen. Like color buttons the user can click on to change the pen color.

  3. Thank you very much for the update to the “How to make a simple 2D drawing app in Swift” tutorial. What I did was add buttons to the view and then manually selected colors for Red, green, and blue. But my program would crash. I even went back and used your exact numbers for the colors and still the program would crash when the user would click on the color buttons I made. I didn’t think of adding a UISegmentControl or the case statement. Just wondering why would a simple buttons with the code:

    {
    red = {0.0/255.0}
    green = 128.0/255.0}
    blue = {255.0/255.0}

    }

    I just tied these to some simple buttons with the background color to match the buttons and the program would crash in the sim and on my devices. Go figure. Do you google chat ? My email is timbojill@gmail.com.

  4. I got another newbie question. I manually set a background image to the UIImageView in the xcode 6 settings. When you run the program it runs great. Allowing the users to write on top of the Image View with the selected background. But when the user presses the refresh button the entire Image View gets wiped including the background image. Is there a way I can fix that ? I also wanted to know if you know of any tutorials on how to have my program recognize the user inputs. For example have the user draw a circle, rectangle, star, etc. and then have the program tell if the user inputted the right shape ?

  5. For the first question, you can place another UIImageView behind the drawing board and set its background image or background color to whatever you want, as I did in the screenshot below. This way, you keep your drawing separated from the background stuff 🙂

    For the second question, you may want to use the UIBezierPath API to draw and recognise shapes, circles and geometry. A good start with the official docs: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIBezierPath_class/index.html

  6. Hi Malek want ask you – how to make more complicate app so there i could sraw with textures(brushes, markers, pencils and others), cause this is very primitive and more complicated features i don’t know were to find

  7. i read the documentation, ask at stackoverflow, looks at other sources
    The problem is that i want to load textures and draw lines with them.
    this could be implemented by using CGContextSetStrokeColorWithColor(context, UIColor(patternImage: UIImage(named:”PencilTexture1″)!).CGColor)

    but at this case i can’t change the color

    UIBEzierPath is similar to cgcontextstrokepath

    there is no methods in uibezierpath to get effects that i want

    as i said it’s similar to cgcontext

    the only things that i found is that similar apps is using objectiveC and open gl es and glkit libraries

    but it is very complicated for me,((( so i search possibility to do it in swift

  8. I want to draw over photo from camera roll, library.. I did that but the problem is the images stretches to fill the draw area, how can I limit the drawing area to the size of image so there won’t be any stretching of image.

Comments are closed.