Supporting SwiftUI with Core Graphics

Nov 22 2022 · Swift 5.5, iOS 15, Xcode 13

Part 1: Supporting SwiftUI with Core Graphics

06. Shading with Pencil

Episode complete

Play next episode

About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 05. Drawing with Pencil Next episode: 07. Create a Thumbnail

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Azimuth & Altitude

As well as detecting force and giving us coalesced touches, Pencil is able to give us the tilt in three dimensions. Using this tilt information, we can shade instead of just draw.


When we shade, instead of drawing a stroke, we’re going to draw a image that looks like a pencil shading.


In CanvasView, right under the color property, add a shading color property that sets up the pencil texture as a UImage with the correct color.

var shadingColor: UIColor = .black {
  didSet {
    let image = UIImage(named: "pencilTexture")!
let tintedImage = UIGraphicsImageRenderer(size: image.size).image { _ in
  image.draw(at: .zero)
shadingColor = UIColor(patternImage: tintedImage)
var color: UIColor = .black {
  didSet {
    shadingColor = color
private let tiltThreshold: CGFloat = .pi / 6
if touch.altitudeAngle < tiltThreshold {
} else {
  if touch.force > 0 {
    lineWidth = touch.force * forceSensitivity
private func lineWidthForShading(touch: UITouch) -> CGFloat {
let maxLineWidth: CGFloat = 60
let minAltitudeAngle: CGFloat = 0.25
let maxAltitudeAngle = tiltThreshold
let altitudeAngle = max(minAltitudeAngle, touch.altitudeAngle)
let normalizedAltitude = (altitudeAngle - minAltitudeAngle) / (maxAltitudeAngle - minAltitudeAngle)
return max(maxLineWidth * (1 - normalizedAltitude), minLineWidth)
🟩var lineWidth: CGFloat

if touch.altitudeAngle < tiltThreshold {
  🟩lineWidth = lineWidthForShading(touch: touch)
} else {
  🟩lineWidth = defaultLineWidth
let minForce: CGFloat = 0
let maxForce: CGFloat = 4
let normalizedAlpha = (touch.force - minForce) / (maxForce - minForce)