Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
An iOS Developer's Guide to SwiftUI

You're reading from   An iOS Developer's Guide to SwiftUI Design and build beautiful apps quickly and easily with minimum code

Arrow left icon
Product type Paperback
Published in May 2024
Publisher Packt
ISBN-13 9781801813624
Length 446 pages
Edition 1st Edition
Languages
Tools
Concepts
Arrow right icon
Author (1):
Arrow left icon
Michele Fadda Michele Fadda
Author Profile Icon Michele Fadda
Michele Fadda
Arrow right icon
View More author details
Toc

Table of Contents (25) Chapters Close

Preface 1. Part 1: Simple Views
2. Chapter 1: Exploring the Environment – Xcode, Playgrounds, and SwiftUI FREE CHAPTER 3. Chapter 2: Adding Basic UI Elements and Designing Layouts 4. Chapter 3: Adding Interactivity to a SwiftUI View 5. Part 2: Scrollable Views
6. Chapter 4: Iterating Views, Scroll Views, FocusState, Lists, and Scroll View Reader 7. Chapter 5: The Art of Displaying Grids 8. Part 3: SwiftUI Navigation
9. Chapter 6: Tab Bars and Modal View Presentation 10. Chapter 7: All About Navigation 11. Part 4: Graphics and Animation
12. Chapter 8: Creating Custom Graphics 13. Chapter 9: An Introduction to Animations in SwiftUI 14. Part 5: App Architecture
15. Chapter 10: App Architecture and SwiftUI Part I: Practical Tools 16. Chapter 11: App Architecture and SwiftUI Part II – the Theory 17. Part 6: Beyond Basics
18. Chapter 12: Persistence with Core Data 19. Chapter 13: Modern Structured Concurrency 20. Chapter 14: An Introduction to SwiftData 21. Chapter 15: Consuming REST Services in SwiftUI 22. Chapter 16: Exploring the Apple Vision Pro 23. Index 24. Other Books You May Enjoy

Drawing with the Canvas

According to Apple documentation, the Canvas is a view type that supports immediate mode drawing. In simpler and more familiar terms, if you have previous experience with UIKit, this is a view that allows you to implement custom bidimensional graphics that you are more accustomed to by using Core Graphics. It allows you to create custom and intricate graphics that you can use in your own user interface.

The programming is basically the same as in Core Graphics. The Canvas requires you to write a closure defining its contents. This closure receives two parameters, GraphicsContext and a size expressed in CGSize that can be used to customize the size of what you want to draw.

You can think about the context as a kind of “handle” of the drawing “pencil” inside the canvas, and you determine what you want to draw by calling the different methods supported by the canvas.

These are graphical primitives that allow you to draw different shapes (outlined and filled), images, text, and even complete SwiftUI views.

For instance, the following example will draw two circles within a square frame:

import SwiftUI
struct CircleView: View {
    var body: some View {
        Canvas { context, size in
            context.stroke(
                Path { path in
                    path.addEllipse(in: CGRect(origin: .zero, size: size))
                },
                with: .color(.green),
                lineWidth: 2
            )
            context.stroke(
                Path { path in
                    path.addEllipse(in: CGRect(origin: .zero, size: CGSize(width: size.width/2, height: size.height/2)))
                },
                with: .color(.green),
                lineWidth: 2
            )
        }
        .frame(width: 300, height: 300)
        .border(Color.blue)
    }
}
struct ContentView: View {
    var body: some View {
        VStack {
            CircleView()
            Text("Canvas View")
        }
        .padding()
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

In CircleView, a canvas is used for drawing shapes. The canvas contains two green ellipses. The first ellipse fills the entire canvas, while the second ellipse is half the size of the first and is drawn inside it. Both ellipses are drawn with a line width of 2 and are green.

The canvas itself has a fixed size of 300 by 300 units and has a blue border outline. ContentView arranges items vertically. At the top is the custom CircleView that displays the two green ellipses, followed by a text label with the title of the screen – “Canvas View”. The result is shown in the following screenshot:

Figure 8.2 – Drawing shapes inside Canvas

Figure 8.2 – Drawing shapes inside Canvas

Canvas allows you to add shapes, which do not offer any interactivity or any way to access the individual elements within the view. However, it may offer better performance for drawing complex shapes. It is also possible to add masks, filters, perform transforms, and control blending. You should not use the Canvas for drawing text primarily or for elements that require individual interactivity. If you need to address each view individually, it is usually better to use individual views inside ZStack or similar.

Important note – caveats on the units of measure

As with Core Graphics, you are expected to use CGFloat and CGSize to indicate typographical unit measures, and you won’t be able to use colors directly if they are needed within a context method. Instead, you will need to convert them to their CG equivalents.

The following example shows how to use opacity, translation, scaling, and displaying text while drawing on Canvas:

import SwiftUI
struct ContentView: View {
    var body: some View {
        Canvas(
            opaque: false,
            colorMode: .linear,
            rendersAsynchronously: false
        ) { context, size in
            context.opacity = 0.6
            let rect = CGRect(origin: .zero, size: size)
            let text = Text(verbatim: "This is not a Text view!").font(.title)
                .bold()
            var resolvedText = context.resolve(text)
            resolvedText.shading = .color(.blue)
            context.draw(resolvedText, in: rect)
            var path = Circle().path(in: rect)
                       context.fill(path, with: .color(.green))
            let rect2 = rect.applying(.init(scaleX: 0.7, y: 0.7).translatedBy(x: 150, y: 300)
            )
                        path = Circle().path(in: rect2)
                        context.fill(path, with: .color(.pink))
            let rect3 = rect.applying(.init(scaleX: 0.2, y: 0.2).translatedBy(x: 150, y: 300)
            )
                        path = Circle().path(in: rect3)
                        context.fill(path, with: .color(.cyan))
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The result is shown in the following screenshot:

Figure 8.3 – A more complex example with Canvas

Figure 8.3 – A more complex example with Canvas

You can use the canvas to not only draw images or text but also draw any View.

Before we do that, we will need to register them using the context.resolveSymbol(id :) and declare them in the symbols closure.

You need to provide an id in the symbols closure, by means of a .tag modifier, and reference it with the id parameter in context.resolveSymbol(id: ).

The following example uses a TextView that is drawn on the Canvas, as shown in the following code example:

import SwiftUI
struct ContentView: View {
    var body: some View {
        Canvas(
            opaque: false,
            colorMode: .linear,
            rendersAsynchronously: false
        ) { context, size in
            let rect = CGRect(origin: .zero, size: CGSize(width:400,height:400))
            if let mySymbol = context.resolveSymbol(id: 0x01) {
                context.draw(mySymbol, in: rect)
            }
        } symbols: {
            Text(verbatim: "Hello World")
                .foregroundColor(.blue)
                .bold()
                .tag(0x01)
                .padding()
                .background( .yellow)
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The result is shown in the following screenshot:

Figure 8.4 – Rendering SwiftUI within Canvas

Figure 8.4 – Rendering SwiftUI within Canvas

In the next section, I will explain how CALayers can be integrated into SwiftUI.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image