This video is only available to subscribers. Start a subscription today to get access to this and 379 other videos.

# Context Transforms

This episode is part of a series: Dive Into Core Graphics.

1. Intro 2 min |

2. Basic Shapes 7 min |

3. Paths 17 min |

4. Colors 9 min |

5. Gradients 11 min |

6. Clipping Paths 6 min |

7. Context Transforms 10 min |

8. Images 7 min |

9. Text 9 min |

10. Offscreen Rendering 12 min |

11. Custom CALayer 13 min |

12. Pie Progress View 7 min |

13. Watermarking Photos 6 min |

14. Working in AppKit 8 min |

## Links

## Drawing a Grid to Help Visualize Transformations

This utility function is a helpful way of visualizing where your drawing is happening. Not the use of the `saveGState`

and `restoreGState`

to keep this function self contained (in other words, no drawing state will leak out of this method and cause other operations to draw incorrectly).

```
func drawGrid(_ context: CGContext) {
context.saveGState()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let color = CGColor(colorSpace: colorSpace, components: [0, 0, 0, 0.2])!
context.setStrokeColor(color)
context.setLineWidth(2)
// Stroke the border
context.stroke(bounds)
// Draw a line every 20px
let increment: CGFloat = 20
for x in 1..<Int(bounds.height / increment) {
// Vertical line
context.move(to: CGPoint(x: CGFloat(x) * increment, y: 0))
context.addLine(to: CGPoint(x: CGFloat(x) * increment, y: bounds.height))
for y in 1..<Int(bounds.width / increment) {
// Horizontal line
context.move(to: CGPoint(x: 0, y: CGFloat(y) * increment))
context.addLine(to: CGPoint(x: bounds.width, y: CGFloat(y) * increment))
}
}
// Stroke grid
context.strokePath()
// Draw top left red square
context.setFillColor(CGColor(colorSpace: colorSpace, components: [1, 0, 0, 0.5])!)
context.fill(CGRect(x: 0, y: 0, width: increment, height: increment))
context.restoreGState()
}
```

## Zooming Out

Zooming out can help you figure out issues where your drawing is happening outside the frame. This is a common situation you might find yourself in when drawing with transforms. For instance if you rotate around the origin 180°, your entire drawing will happen outside the visible rect.

When performing transformations like zoom, it is important to remember that it happens at the *anchor point*, or origin of the context. In order to zoom out from the center, we'll first have to translate so that the anchor point is at the center of the view:

```
context.translateBy(x: bounds.midX, y: bounds.midY)
```

Then we can apply our scale transformation:

```
context.scaleBy(x: 0.5, y: 0.5)
```

And then we just need to jump back to where we were:

```
context.translateBy(x: -bounds.midX, y: -bounds.midY)
```

## Flipping Horizontally

This is a common thing you might do when showing an image of someone through the camera. Since most people are accustomed to looking at themselves in a mirror, you can simulate this effect by horizontally flipping the image.

```
Flip horizontally
context.translateBy(x: bounds.midX, y: bounds.midY)
context.scaleBy(x: -1, y: 1)
context.translateBy(x: -bounds.midX, y: -bounds.midY)
drawGrid(context)
```

Whenever you are dealing with scale transforms, be careful to use `1`

(not `0`

) to represent a no-op, because otherwise that will result in drawing nothing!

You might also use this technique to create reflections of objects by drawing them again, scaled `-1`

in the y dimension in combination with a gradient overlay.

## Rotating

If we want to rotate 45° we first have to convert that to radians. In radians, 2π is equal to the entire circle, so 2π = 360. to convert this to radians:

```
// 45° = 2π * (45/360)
// or (simplified)...
// 45° = π / 180
```

It helps to think of thinks in radians, so if 2π is the whole circle, π is half the circle, π/2 is 1/4 circle, and π/4 is 1/8th the circle (or 45°).

Again we use the trick to translate to the center so we rotate around the center, then back again after the transformation:

```
// Rotate -45°
context.translateBy(x: bounds.midX, y: bounds.midY)
context.rotate(by: .pi / -4)
context.translateBy(x: -bounds.midX, y: -bounds.midY)
drawGrid(context)
```

We can then draw things and they will all be rotated:

```
// Draw blue square
context.setFillColor(CGColor(colorSpace: colorSpace, components: [0, 0, 1, 1])!)
context.fill(CGRect(x: 60, y: 60, width: 200, height: 200))
```

This ends up looking like a diamond since we are rotated.

It is important to rotate back (or use the graphics state) to go back to how we were:

```
// Rotate 45° (back to regular)
context.translateBy(x: bounds.midX, y: bounds.midY)
context.rotate(by: .pi / 4)
context.translateBy(x: -bounds.midX, y: -bounds.midY)
```

Now we can draw other things that *aren’t* rotated.

```
// Draw green square
context.setFillColor(CGColor(colorSpace: colorSpace, components: [0, 1, 0, 0.5])!)
context.fill(CGRect(x: 60, y: 60, width: 200, height: 200))
// Draw yellow square
context.setFillColor(CGColor(colorSpace: colorSpace, components: [1, 1, 0, 0.5])!)
context.fill(CGRect(x: 300, y: 300, width: 20, height: 20))
```