NEW! Pre-order my latest book, Testing Swift! >>

< Previous: Drawing into a Core Graphics context with UIGraphicsImageRenderer   Next: Transforms and lines >

Ellipses and checkerboards

Add a case to your redrawTapped() method to call a new method: drawCircle(). This will… wait for it… draw a circle. So, your switch/case should look like this:

switch currentDrawType {
case 0:
    drawRectangle()

case 1:
    drawCircle()

default:
    break
}

There are several ways of drawing rectangles using Core Graphics, but the method we used in drawRectangle() is particularly useful because in order to draw a circle we need to change just one line of code. This is because drawing circles (or indeed any elliptical shape) in Core Graphics is done by specifying its rectangular bounds.

So, where before you had:

ctx.cgContext.addRect(rectangle)

…you can now use this:

ctx.cgContext.addEllipse(in: rectangle)

It even has the same parameters! So, the full drawCircle() method is this:

func drawCircle() {
    let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))

    let img = renderer.image { ctx in
        let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)

        ctx.cgContext.setFillColor(UIColor.red.cgColor)
        ctx.cgContext.setStrokeColor(UIColor.black.cgColor)
        ctx.cgContext.setLineWidth(10)

        ctx.cgContext.addEllipse(in: rectangle)
        ctx.cgContext.drawPath(using: .fillStroke)
    }

    imageView.image = img
}

Run the app now and click the button once to make it draw a circle. Notice how the stroke around the edge appears to be clipped at the top, right, bottom and left edges? This is a direct result of what I was saying about stroke positioning: the stroke is centered on the edge of the path, meaning that a 10 point stroke is 5 points inside the path and 5 points outside.

Now that we're drawing a circle, you can see how Core Graphics is clipping our border at the edges.

The rectangle being used to define our circle is the same size as the whole context, meaning that it goes edge to edge – and thus the stroke gets clipped. To fix the problem, change the rectangle to this:

let rectangle = CGRect(x: 5, y: 5, width: 502, height: 502)

That indents the circle by 5 points on all sides, so the stroke will now look uniform around the entire shape.

A different way of drawing rectangles is just to fill them directly with your target color. Add a "case 2" to your switch/case that calls a method named drawCheckerboard(), and give it this code:

func drawCheckerboard() {
    let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))

    let img = renderer.image { ctx in
        ctx.cgContext.setFillColor(UIColor.black.cgColor)

        for row in 0 ..< 8 {
            for col in 0 ..< 8 {
                if (row + col) % 2 == 0 {
                    ctx.cgContext.fill(CGRect(x: col * 64, y: row * 64, width: 64, height: 64))
                }
            }
        }
    }

    imageView.image = img
}

The only piece of code in there that you won't recognize is fill(), which skips the add path / draw path work and just fills the rectangle given as its parameter using whatever the current fill color is. You already know about ranges and modulo, so you should be able to see that this method makes every other square black, alternating between rows.

There are two things to be aware of with that code. First, we're filling every other square in black, but leaving the other squares alone. As we haven’t specified that our renderer is opaque, this means those places where we haven't filled anything will be transparent. So, if the view behind was green, you'd get a black and green checkerboard. Second, you can actually make checkerboards using a Core Image filter – check out CICheckerboardGenerator to see how!

Drawing a checkerboard with Core Graphics is just a matter of drawing squares with alternating colors.

Upgrade to the premium experience

Get all 40 projects in PDF and ePub, plus exclusive content that will take your Swift learning to the next level – buy the Hacking with Swift book today!

< Previous: Drawing into a Core Graphics context with UIGraphicsImageRenderer   Next: Transforms and lines >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>