[Slide 01]
If you followed along with our Drawing in iOS with SwiftUI course, you built a functioning mind map app. It’s a little app to get ideas down. We can add our ideas to cells, and we can even make conceptual drawings with the drawing pad.
If you didn’t follow along with that course, I recommend you take some time to get familiar with the starter project before you start the next episode.
[Slide 02]
Because in this course, we’re going to enhance that app with help from UIKit and Core Graphics.
After all, there are some thing that SwiftUI just can’t do yet, like capture Apple Pencil touches.
We’ll find out how to integrate UIKit views with SwiftUI.
We’ll use UIKit touch methods to capture Apple Pencil drawing, and make the whole drawing view a UIControl that we can plug into any of our future SwiftUI apps.
We’ll also reach into Core Graphics to upgrade our color picking controls and capture drawing images for thumbnails.
[Slide 03 - Declarative vs Imperative]
It might help us to review some of the difference between SwiftUI and UIKit, before we start trying to mix them together.
SwiftUI is declarative. That means that you set up data and then describe how you want the data to be displayed, and SwiftUI goes away and works out how to render all the views with the given data.
UIKit on the other hand is imperative. UIKit consists of classes with properties. You set each of the properties on a UIView, or subclass the UIView if you have custom properties.
UIKit and SwiftUI are totally different paradigms! So, let’s take an high level look at what a UIKit UIView consists of and how the system renders one.
[Slide 04]
View Hierarchy
As I just mentioned, a UIView is a class. It’s part of a view hierarchy.
When you design a view, every element on that view will be another UIView. You add new subviews to your UIView such as UILabels and UIButtons.
When it comes to positioning a UIView, it has properties for absolute coordinates. Whereas in SwiftUI you give a view a modifer for preferred positioning.
[Slide 05]
Core Animation Layers
A UIView class has a lot of properties. However, there aren’t any contents in a UIView that get rendered.
Each UIView has one or more Core Animation Layers, and when it comes to rendering time, the GPU composites all these CALayers together and renders them.
This code example shows placing an image into a layer’s contents. Generally you’d use UIImageViews for this, as they create an image layer automatically, but you might have specialized needs where you want to composite many layers together. It may be more efficient to use layers rather than UIViews.
[Slide 06]
Core Animation predates UIKit, and uses the Core Graphics API for its content. For example, here is a UIView with a blue background that uses UIColor, and three CALayers with different cgColors.
UIKit objects generally provides a cg property. For example, the UIColor has a cgColor property, and UIImage has a cgImage property.
[Slide 07]
Here’s how that layer setup will be rendered on screen. So why would you use Core Animation Layers? Core Animation is fast and flexible, and of course you can animate each layer separately.
There are many different types of Core Animation layers such as layers for vector shapes, particle effects and gradients.
Because a UIView always has at least one layer, Core Animation is always there in the background. But that’s the last you’ll hear about layers in this course.
We do have other resources where you can discover more about CALayers. And I’ll link to those in the notes for this episode. So, what’s an alternative?
[Slide 08]
Core Graphics
Instead of layering, you might want to build up a single bitmap image. In this case you would use a Core Graphics bitmap context.
Here is a context made up of some text, fills, strokes and paths. Unlike layers, when you draw an element into a context, it becomes part of the context and you can’t separate out the context into separate elements.
[Slide 09]
UIView Drawing
A UIView has a draw method that it automatically calls whenever it needs to be drawn. This isn’t every frame, just when the UIView appears or when the system marks the view as needing to be redrawn.
Inside this draw method you have access to the view’s CGContext, and can do drawing directly into it. Here I’ve filled the view’s context with yellow and then drawn an image into the context.
[Slide 10]
UIGraphicsImageRenderer
Any time you want to create an image and you don’t have a current CGContext, you can create one with UIGraphicsImageRenderer.
This is how you can create a UIImage. Here I’ve just filled the CGContext with blue. In the next episode, we’ll draw a grid image for our app’s background using this technique.
[Slide 11]
Hosting a UIView
Fortunately you can use UIKit’s UIViews in SwiftUI almost as easily as a SwiftUI View using a structure that conforms to UIViewRepresentable.
This is the intermediary between a SwiftUI View and a UIKit UIView. When we write our UIKit drawing pad, we’ll create a UIViewRepresentable structure to host it, and you’ll see how this all fits together.