iOS Graphics System consists of 3 frameworks:

  1. UIKit - provide views on a high level
  2. Core Graphics - lower level drawing support within UIKit views
  3. Core Animation - ability to apply animation and transformation to UIKit views

Performance of UIImage vs Drawing with code

This answer explained well when is GPU used, and when is CPU used.

Displaying a UIImage is (generally) faster, because after loading the image file (via CPU), the image is loaded onto the GPU. If you now display the image 100 times, it will be very fast, because the GPU already contains the image/texture.

Drawing with code via Core Graphics is slower, because the drawing code happens in the CPU, before being loaded onto the GPU. If you are going to draw 100 times, there will be 100 trips from the CPU to the GPU.

Quoting from Apple Doc:

The use of custom drawing code should be limited to situations where the content you display needs to change dynamically.

If you need to combine standard UI elements with custom drawing, consider using a Core Animation layer to superimpose a custom view with a standard view so that you draw as little as possible.

Animation Effects

Before we jump into the large chunk of this post on drawing images, let’s know how animation effects can be applied to the image.

Core Animation has a layer object, and this is actually a model that encapsulates animations properties - geometry, timing and visual properties.

By modifying this model, you achieve animation easily. The actual rendering is taken care of, and optimized for you.

Drawing/Creating Paths

UIBezierPath is the class to create vector-based shapes.

You create paths with 2 types: straight lines and curves

1. Straight Lines

Assume you have 2 points (CGPoint), you can draw a line with addLineToPoint:

let path = UIBezierPath()

2. Curves

There are 2 types of curves:

Types of curves

  1. Cubic curve - use addCurve(to:controlPoint1:controlPoint2:)
  2. Quadratic curve - use addQuadCurve(to:controlPoint:)

The difference is that a cubic curve has 2 control points.

Bezier curve has complex mathematical relationship, if you are interested.

Underlying CGPathRef

UIBezierPath is really just a wrapper for CGPathRef data type.

You can create CGPaths directly then assign to a UIBezierPath.

Fill & Stroke

With a path constructed, you can then render by filling and stroking with colors.

The code below with fill with red, and stroke a blue line 2 point wide.


path.lineWidth = s2

There is one thing about filling that you should know. The even-odd fill rule determines if a hole in a path is to be filled or not. By default, usesEvenOddFillRule is false, so usually the hole will be filled.

Drawing an Image

You can also draw an image. Read the section on performance earlier. If you can use UIImageView, then you should.

Otherwize, you can use the draw methods in UIImage:


CGContext Transformation

You can change the Current Transformation Matix (CTM) for the graphics context.

For example, you can scale the drawing by 50%, then translate (20, 20):

let context = UIGraphicsGetCurrentContext()

CGContextScaleCTM(context, 0.5, 0.5)
CGContextTranslateCTM(context, 20, 20)

// Draw and render your path etc, with respect to the origin.
// However, the CTM transformation will affect the render.


Notice that you should save and restore the graphics states.

Rendering UIImage

These are the steps to generate an UIImage:

let size = CGSize(width: width, height: height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()

// Create your bezier path, fill, stroke, etc..

let img = UIGraphicsGetImageFromCurrentImageContext()

It is important that you pass 0.0 to UIGraphicsBeginImageContextWithOptions so that the appropriate bitmap size is created for the (eg retina) device automatically.




Back to Home