Supporting SwiftUI with Core Graphics

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

Part 1: Supporting SwiftUI with Core Graphics

02. Create a CGImage

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 01. Anatomy of a View Next episode: 03. Host a UIView in a SwiftUI View

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.

Transcript: 02. Create a CGImage

Before we even touch the code, I’d like to point out a very nice way to debug your view hierarchy. Run your app in the simulator.

Click the Debug View Hierarchy icon on the debug bar.
Xcode lays out all your views and subviews, and when I drag the view, you can see them laid out in 3d.

On the left, all the views are listed in a hierarchy, and you can see how the SwiftUI views are hosted in a UIWindow. Open up the Object Inspector and you can see that the SwiftUI views are hosted by a UIHostingController.

If one of your views is showing up in the wrong place, you can use this debugger to visualize all your views and work out why they are misplaced.

This is the GeometryReader. Notice that unlike the Color view, it’s not full screen, which might cause a problem later on.

ContentView.swift

So we can fix that in ContentView.

GeometryReader {
   ...
}
🟩.ignoresSafeArea()

And run the app again. And click the Debug View Hierarchy icon. And the GeometryReader is now full screen. Let’s clean up this debug view… And I’ll show you something in our running app, here.

We’re going to replace this plain colored background with a custom grid.

SwiftUI has a nice ImagePaint structure to repeat an image so all we need to do is draw two straight lines into an image Renderer.

When we repeat these lines over the screen, they appear to be a grid, like this.

But the lines will be nice and straight!

GridView.swift

So, create a new SwiftUI View file called GridView.swift

First create a new method with an image renderer in it. Here we create an image renderer and we return the renderer’s image from the method. The image comes from any drawing we do inside this context.

func gridImage(size: CGSize) -> UIImage {
  let width = size.width
  let height = size.height
  return UIGraphicsImageRenderer(size: size).image { context in
    
  }
}

The size parameter is the size that each grid square should be, so we can make the grid larger and smaller. Inside the renderer’s closure, set the drawing color:

UIColor.systemTeal.setStroke()

I want to draw a Path for the lines, and UIKit has an equivalent class to SwiftUI’s Path called UIBezierPath. The API commands are very similar.

Create a UIBezierPath and draw the path. Just as we did creating SwiftUI paths, we move to the first point, and then add points to lines, and then stroke the path to draw it into the context.

let path = UIBezierPath()
path.move(to: CGPoint(x: width, y: 0))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.stroke()

We’ve created a simple image; now we can draw it into a SwiftUI Rectangle! In body, replace Text with a Rectangle to fill up the whole view.

Rectangle()
  .ignoresSafeArea()

If you reload the preview, you’ll see we’ve got a black rectangle over the whole screen. At the top of body, create a SwiftUI Image from the gridImage() method.

let size = CGSize(width: 30, height: 30)
let image = Image(uiImage: gridImage(size: size))
...

Use this image as a fill for the Rectangle.

Rectangle()
  .fill(ImagePaint(image: image))

ImagePaint repeats the image all over the background so we now have a grid just from our two straight lines and we can control the size of the grid by setting a new size for the image.

To give us more control over how this looks in dark mode as well, let’s wrap this in a ZStack. Add a tertiary system background color at the top so it will show behding the grid. And move the safe area modifier down so it applies to the whole stack.

ZStack {
  Color(uiColor: .tertiarySystemBackground)
  Rectangle()
    .fill(ImagePaint(image: image))
}
.ignoresSafeArea()

BackgroundView.swift

Open up BackgroundView.swift, and change the color in the ZStack to be the GridView

GridView()

That looks a lot more professional!