0% found this document useful (0 votes)
80 views196 pages

SwiftUI Views Jumpstart 2019-10-29

Uploaded by

MohaMed Rebin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
80 views196 pages

SwiftUI Views Jumpstart 2019-10-29

Uploaded by

MohaMed Rebin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 196

VISUAL QUICKSTART REFERENCE

SwiftUI Views
Free Sample SwiftUI 1.0

Mark Moeykens
www.bigmountainstudio.com YOUR COMPREHENSIVE VISUAL
1 REFERENCE GUIDE Big Mountain Studio
Book Version: 2019-10-29

www.bigmountainstudio.com 2
This book is dedicated to
My wife and daughter for all of their
continued support through he long
hours of working nights and weekends
to bring this book to you.

Special thanks to
My patrons, on patreon.com/bigmountainstudio ,who
continue the fine tradition of supporting content
creators, providing feedback, suggestions, corrections
and encouragement.

www.bigmountainstudio.com 3
TABLE OF CONTENTS
The table of contents should be built into your ePub and PDF readers. Examples:

Books App Adobe Acrobat Reader Preview


Foreword by Meng To
I have been teaching Swift to designers and design to coders for years now. SwiftUI is an incredible
step in the direction of combining these two fields. Using Xcode to create apps in SwiftUI is feeling
more and more like using a design tool.

If you are looking for a reference guide when using SwiftUI to build your apps, then Mark has you
covered with this excellent resource here. He has been sharing his Swift knowledge for years in an
easy to understand manner and this book continues to follow this tradition.

I wish you the best in your journey to learning SwiftUI. This technology is already powerful and will
only get better with time. I have enjoyed learning SwiftUI and I think you will too.

Meng To
designcode.io
HOW TO USE
This is a visual REFERENCE GUIDE. Find a screenshot of something you
want to learn more about or produce in your app and then read it and look
at the code.

Read what is on the screenshots to learn more about the views and what
they can do.
You can also read the book from beginning to end. The choice is yours.
Conventions

CONVENTIONS

www.bigmountainstudio.com 7
Conventions

Code Formatting
Throughout this book, you may notice I don’t always follow the same formatting conventions. This is due to limited vertical space.
For example, on one page you may see code formatted like this (pseudo-code):

NewView()
.modifyTheView1()
.modifyTheView2()

And then on another page, you see code formatted like this:

NewView().modifyTheView1().modifyTheView2()

Other times, functions may be on the same line as the closing brace:

NewView {
...
}.modifyTheView2()

Or on the next line:

NewView {
...
}
.modifyTheView2()

In the end, how the code is formatted in your project is up to you. These inconsistencies are strictly due to limited page space.

www.bigmountainstudio.com 8
Conventions

Omitting Code
When using SwiftUI, the views (screens) are represented in a struct, inside a body property. (More on this later.) This will become apparent when you
add your first SwiftUI file to your project.

In most examples, you will see the struct and body property are missing. Again, this is due to limited vertical spacing. The main thing to remember is
that the relevant code is always shown.

For example, I may take this view here (pseudo-code):

struct MyView {
var body {
NewView()
.modifyTheView1()
.modifyTheView2()
}
}

And show only the relevant code:

NewView()
.modifyTheView1()
.modifyTheView2()

When space is limited, I omit the unnecessary code and show an ellipsis:

struct MyView {
var body {
... // Unnecessary code omitted
NewView()
}
}
www.bigmountainstudio.com 9
SWIFTUI

10
Basic Concepts

Basic Concepts

If you are absolutely new to SwiftUI, you should definitely read through this chapter to establish some basic concepts that
you can think with.

www.bigmountainstudio.com 11
Basic Concepts

Views And Modifiers

In SwiftUI, you build a UI with Views and then View


you change those views with Modifiers. Modifiers:
• Large title text size

View
Modifiers:
• Title text size View
• Gray text color Modifiers:
• Title text size
• Orange background color
View • Stretched to fit device width
Modifiers:
• Title text size
• White text color
• Orange background
color
• Rounded corners
• Shadow

www.bigmountainstudio.com 12
Basic Concepts

Containers - Vertical Layout Container

Views can be organized in containers. Some VStack View


containers organize views in one direction. This is
Modifiers:
called Stack.
• Padding around it
• Orange border
Here is an example of a Vertical Stack or as
• 20 point corner radius
SwiftUI calls it, a “VStack”.
• 4 point line width
• Add 40 points of spacing between
Stacks are views too. They are views that can
the views within it
have modifiers applied to them.

www.bigmountainstudio.com 13
Basic Concepts

Horizontal Layout Container

There is another stack that can organize views


horizontally. SwiftUI calls this horizontal stack an
HStack.

HStack View
Modifiers:
• Padding around it
• Orange border
• 20 point corner radius
• 10 point line width
• Add 20 points of spacing between
the views within it

www.bigmountainstudio.com 14
Basic Concepts

Depth Layout Container

The last stack view will organize views one on top


of another. This is called the Depth Stack or
ZStack.

ZStack View
Modifiers:
• Padding around it
• Orange border
• 20 point corner radius
• 2 point line width

www.bigmountainstudio.com 15
Basic Concepts

Layout Examples

Now that you know these layout stacks, you can start to
guess how views like these might be arranged using SwiftUI.

VStack

HStack

In this book, you will be seeing hundreds of layout examples.


Pretty soon, it will become a natural ability of yours to
recognize layout.

www.bigmountainstudio.com 16
Basic Concepts

Relationships - Parent & Child

It is common in programming to express a


hierarchy of objects as a parent and child
Parent View (HStack)
relationship.

In this book, I use this concept to express


relationships with SwiftUI views.
It is beneficial to know that Apple refers to child
views that have no children of their own as “leaf
In this example, you have an HStack view that
views”.
contains two child views within it. The HStack is
the parent.
These two child views are leaf views because they
contain no other views within them.

Child Views inside


Leaf View

www.bigmountainstudio.com 17
Basic Concepts

Relationships - And Modifiers

Some modifiers can be set on the parent view and it


will also apply to all children in the container. VStack View
Modifiers:
In this example, the font size is set on the parent and
• Font size is 28 points
the child views use it.

The parent text does not use the font size because it
overrides it with a larger font size.

Text View
Modifiers:
• Font size is 32 points

www.bigmountainstudio.com 18
Understanding the Syntax

Understanding the Syntax

If you have used Swift in the past, then the SwiftUI syntax may look a little different.

It may not be readily apparent just how this code can even compile. This chapter is to help you understand how the code is
able to work.

www.bigmountainstudio.com 19
Understanding the Syntax

The View
struct BasicSyntax: View {
var body: some View {
Text("Hello World!") // Adds a text view to the screen
}
}

Views in SwiftUI are structs that conform to the View protocol. There is just one property to implement,
the body property.

If “body” is a property then where is the “get” and the “return” syntax?

www.bigmountainstudio.com 20 Free Sample (Get the Full Version Here)


Understanding the Syntax

Property Getters
struct Person {
Properties can have a getter and setter. But // Computed read-only property (no set, value is not stored)
var personType: String {
when a property has no setter, it’s called a
get {
“read-only” property. And when the return "human"
}
property does not store a value, it is called a
}
“computed” property. This is because the }
value is computed or generated every time
the property is read.
In this example, personType is a computed
read-only property. // Change 1 - Remove the return
struct Person {
var personType: String {
You can further simplify this property in get {
"human"
two ways: }
}
1. When the code inside the get is a single }
expression (one thing), the getter will just
return it automatically. You can remove
return.
See “Change 1” in the code example.
// Change 2 - Remove the get
var personType: String {
2. When a property is read-only (no setter), "human"
}
we can remove the get. }
Just know that these changes are
optional.

www.bigmountainstudio.com 21 Free Sample (Get the Full Version Here)


Understanding the Syntax

SwiftUI With Property Getters

Since these property changes are optional, you can, for example, write the previous SwiftUI syntax with
a get and return inside the body property. This might look more familiar to you now:

// SwiftUI with the get and return keywords


struct BasicSyntax: View {
var body: some View {
get {
return Text("Hello World!")
}
}
}

Looking at this code again, you notice the some keyword here.
Normally, when defining a type for a property, you wouldn’t see this word.

So, what does the some keyword do?

www.bigmountainstudio.com 22 Free Sample (Get the Full Version Here)


Understanding the Syntax

Opaque Types
struct BasicSyntax: View {
var body: some View {
Text("Hello World!")
}
}

Opaque Types
The keyword some is specifying that an opaque type is being returned. In this case, the opaque type is
View. So why is the type called “opaque”? Well, the English definition for the word “opaque”, when
referring to languages, means “hard or impossible to understand.” And this is true here because opaque
types hide the value’s type information and implementation details. This will certainly make it “hard or
impossible to understand” but still usable.
When this View (BasicSyntax) is used by iOS to draw the screen, it doesn’t have to know that, in this
example, the type Text is being returned. It is OK with just knowing that some View is being returned
and can use it to draw the screen.
And so you can return anything in that body property as long as it conforms to the View protocol.

For more information on Opaque Types, I recommend referring to the Swift Programming Language
documentation.

There is another important thing to know about opaque types too...

www.bigmountainstudio.com 23 Free Sample (Get the Full Version Here)


Understanding the Syntax

Opaque Types (some Keyword)

You already know from the previous page that what is returned from the body property is something
that conforms to the View protocol.
But what you also need to know is when returning an opaque type (using the some keyword), is that all
possible return types must all be of the same type.
In most cases you are only returning one type. But you might have a scenario like this:

struct UnderstandingTheSomeKeyword: View {


var isYellow = true

// The keyword "some" tells us that whatever we return, it has to:


// 1. Conform to the View protocol
// 2. Has to ALWAYS be the same type of View that is returned.
var body: some View { // ERROR: Function declares an opaque return type, but the
return statements in its body do not have matching underlying types
if isYellow {
return Color.yellow // Color type does not match the Text type
}
return Text("No color yellow") // Text type does not match the color type
}
}

The solution would be to change the views returned so they are all the same type.

www.bigmountainstudio.com 24 Free Sample (Get the Full Version Here)


Understanding the Syntax

View Containers
struct Example: View {
var body: some View {
VStack {
Text("Hello World!")
Text("This Vertical Stack is using a function builder")
}
}
}

So far, you have learned that body is a computed read-only property and can only return ONE object
that is some View. What if you want to show multiple views though?

You learned earlier about the concept of “containers” views. These are views that can contain other
views. Remember, the body property can only return one view. You will get an error if you try to return
more than one view in the body property.

In the example above, the VStack (Vertical Stack) is that one view being returned. And that vertical stack
is a container with two more views inside of it.

The VStack is using a “trailing closure” which just means that it is a code block is passed into the
initializer to be run by the VStack. You have probably seen this before in Swift, this is not new.

What is new in Swift is the ability to create multiple, new views within the constructor like this. Before we
get into this though, let’s better understand how this constructor works.

www.bigmountainstudio.com 25 Free Sample (Get the Full Version Here)


Understanding the Syntax

View Container Initialization


struct Example: View {
In Swift, you usually see parentheses during
var body: some View {
initialization but with a trailing closure, the VStack {
parentheses are optional. Text("Hello World!")
You can add them and the code will still work Text("This Vertical Stack is using a function builder")
just fine. }
See “Change 1” in the code example. }
}
This change may start looking more familiar to
you. // Change 1 - Add parentheses and parameter name
Now, the question is: struct Example: View {
var body: some View {
How does the VStack know how to accept the VStack(content: {
Text("Hello World!")
multiple views like this? Text("This Vertical Stack is using a function builder")
})
This is new in Swift. To better understand this, }
take a look at the VStack’s initializer. }

The alignment and spacing parameters are // VStack initializer


optional, that is why you don’t see them in the init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil,
examples above. But notice before the @ViewBuilder content: () -> Content)
content parameter there is @ViewBuilder
syntax.

This is what allows you to declare multiple


views within the content parameter’s
closure.

www.bigmountainstudio.com 26 Free Sample (Get the Full Version Here)


Understanding the Syntax

@ViewBuilder Parameter Attribute


struct ViewBuilderExample: View {
The @ViewBuilder parameter attribute var body: some View {
VStack {
allows Swift to build multiple child views
Text("View 1")
from within a closure. Text("View 2")
Text("View 3")
Text("View 4")
How many child views can I build within Text("View 5")
Text("View 6")
a closure? Text("View 7")
Text("View 8")
Text("View 9")
Text("View 10")
The way this functionality is set up, you
Text("View 11") // Will cause an error
can only initialize a maximum of ten (10) }
}
views. In the example here, you will get }
an error because of the 11th view.
struct ViewBuilderExample: View {
var body: some View {
What if I need more child views? VStack {
... // Text views 1 - 5
If you need to declare more child views Text("View 6")
Text("View 7")
for your user interface, then you will have Text("View 8")
to use another view container, such as Text("View 9")
VStack { // The VStack is now the 10th view
another VStack. (You will be seeing more Text("View 10")
options for containers in this book.) Text("View 11")
}
}
In the second example, I use another }
}
VStack to contain text views 10 and 11.

www.bigmountainstudio.com 27 Free Sample (Get the Full Version Here)


My Template

If you are completely new to SwiftUI you may wonder what a lot of this code means right at the beginning of the book. I
have a certain “template” that contain a title, subtitle and a short description on most screens.

I will take you through step-by-step on how I build this template that I use throughout the book. I will describe each one
only briefly because each modifier I apply to the views are described in more detail throughout the book within their own
sections.

www.bigmountainstudio.com 28
My Template

My Basic Template

Here is my basic template I use throughout the book to explain views and modifiers.

In the next pages I’m going to explain how this is built in SwiftUI. I want to make sure you understand
these parts because you will see them everywhere in this book.

I want to remove any confusion right at the beginning so it doesn’t get in your way to learning the
topics in the book.

Let’s start with the title.

www.bigmountainstudio.com 29 Free Sample (Get the Full Version Here)


My Template

Starting with the Title


struct Title: View {
var body: some View {
Text("Title") // Create text on the screen
.font(.largeTitle) // Use a font modifier to make text large
}
}

Here, you have a Text view. You want to make it larger so you use the font modifier so you can set the
size to a SwiftUI preset size called largeTitle (this is the largest preset size).

There are more ways you can change the size of text that are covered in this book in the Control Views
chapter, in the section called Text.

Next, we want to add the subtitle.

www.bigmountainstudio.com 30 Free Sample (Get the Full Version Here)


My Template

Adding the Subtitle and VStack


struct Subtitle: View {
var body: some View {
// Only one view can be returned from the body property.
// Add 20 points between views within this container.
VStack(spacing: 20) {

Text("Title")
.font(.largeTitle)

Text("Subtitle")
.font(.title) // Set to be the second largest font.
.foregroundColor(Color.gray) // Change text color to gray.
}
}
}

The body property can only return one view. You will get an error if you declare two views. So, we
need to use a container view that will contain the two text views. The vertical stack (VStack) is the
perfect choice here. It has an optional parameter you can use in its initializer to specify how many
points of spacing you want in between views. (Note: spacing does not add spacing to the top or bottom of
the VStack.)

The subtitle is another text view. This time, you set the size to be the second largest preset size with the
title parameter.

Finally, you modify the view to change the text color to gray. (Note: instead of using Color.gray you can
also use just .gray.)

Now, let’s add the description text.


www.bigmountainstudio.com 31 Free Sample (Get the Full Version Here)
My Template

Add the Description with a Background Color


struct Description1: View {
var body: some View {
VStack(spacing: 20) {
Text("Title")
.font(.largeTitle)

Text("Subtitle")
.font(.title)
.foregroundColor(.gray)

Text("Short description of what I am demonstrating goes here.")


.font(.title)
.foregroundColor(Color.white)
.background(Color.blue) // Add the color blue behind the text.
}
}
}

With the description text view, you are now familiar with the font and foregroundColor modifiers.
But now you want to add a color behind the text. So you use the background modifier to set a color.

The important thing to notice here is it is not a backgroundColor modifier. That does not exist. It is a
background modifier because it adds a layer behind the view.

Color.blue is actually a view. So the background modifier is adding a blue view on a layer behind the
text.

We want this view to extend to the edges of the screen. So let’s add that next.

www.bigmountainstudio.com 32 Free Sample (Get the Full Version Here)


My Template

Adding a Frame Modifier


struct Description2: View {
var body: some View {
VStack(spacing: 20) {
Text("Title")
.font(.largeTitle)

Text("Subtitle")
.font(.title)
.foregroundColor(.gray)

Text("Short description of what I am demonstrating goes here.")


.frame(maxWidth: .infinity) // Extend until you can't go anymore.
.font(.title)
.foregroundColor(Color.white)
.background(Color.blue)
}
}
}

To extend the text to the edges of the device, we use the frame modifier. You don’t need to set a fixed
value. Instead, you can just modify the text view and say its frame’s maximum width can extend to
infinity until it hits its parent’s frame and then will stop. Its parent’s frame is the VStack.

Notice the frame modifier is now the first modifier. In SwiftUI, the order of modifiers matter.

This is looking good. It would look better though if there was more space around the text that pushed
out the blue background.

www.bigmountainstudio.com 33 Free Sample (Get the Full Version Here)


My Template

Add Padding Around the Text View


struct Description3: View {
var body: some View {
VStack(spacing: 20) {
Text("Title")
.font(.largeTitle)

Text("Subtitle")
.font(.title)
.foregroundColor(.gray)

Text("Short description of what I am demonstrating goes here.")


.frame(maxWidth: .infinity)
.font(.title)
.foregroundColor(Color.white)
.padding() // Add space all around text text
.background(Color.blue)
}
}
}

Use the padding modifier to add space around a view. Remember, the order of modifiers matter. You
can add the padding modifier anywhere as long as it is BEFORE the background modifier. If it was
after the background, it would add space around the blue background. We want the space between the
text and the background.

Something is wrong now. The text view’s height can’t be calculated correctly. When adding modifiers
that affect the size of a view, this can sometimes happen. We want to tell iOS to give priority to the
calculation of space for the description text view first. Then calculate space for the other text views
second.

www.bigmountainstudio.com 34 Free Sample (Get the Full Version Here)


My Template

Giving Priority to Calculating Layout Spacing


struct Description4: View {
var body: some View {
VStack(spacing: 20) {
Text("Title")
.font(.largeTitle)

Text("Subtitle")
.font(.title)
.foregroundColor(.gray)

Text("Short description of what I am demonstrating goes here.")


.frame(maxWidth: .infinity)
.font(.title)
.foregroundColor(Color.white)
.padding()
.background(Color.blue)
.layoutPriority(1) // This text view has the highest priority over the
other views in this VStack for the calculation of the space it needs on the screen.
}
}
}

Using layoutPriority you are telling iOS to figure out the space requirements for this text view first
over the other text views in the parent (VStack). The default layout priority of views is zero, so the
higher the number, the greater the priority.

Note: The layoutPriority is not always needed. SwiftUI has been making improvements to
automatically handle this. So, only add layoutPriority if you find you need it.

There is one more concept I want to explain. Notice that the subtitle and description text views are using
the same font modifier setting. You can actually refactor this.

www.bigmountainstudio.com 35 Free Sample (Get the Full Version Here)


SwiftUI Basics

Now that you understand this basic template I use for demonstrating topics, I will start using it. Be sure to read what is on
each screenshot (or find the text in the code to read).

www.bigmountainstudio.com 36
SwiftUI Basics

Refactoring
struct Refactoring: View {
var body: some View {
VStack(spacing: 20) {
Text("Refactoring")
.font(.largeTitle)

Text("Reusing Modifiers")
.foregroundColor(.gray)

Text("You can put common modifiers on the parent views to be applied to all
the child views.")
.frame(maxWidth: .infinity)
.foregroundColor(Color.white)
.padding()
.background(Color.blue)
.layoutPriority(1)
}
.font(.title) // This font style will be applied to all text views inside the
VStack.
}
}

Here, the title font is being applied to all three text views inside of the parent view (VStack).

Why isn’t the title text view affected?

Because the title text view sets the font again, it overrides the title size with the largeTitle size.

www.bigmountainstudio.com 37 Free Sample (Get the Full Version Here)


SwiftUI Basics

Short Introduction to Symbols


struct SymbolsIntro: View {
var body: some View {
VStack(spacing: 20) {
Text("Images")
.font(.largeTitle)
Text("Using SF Symbols")
.foregroundColor(.gray)

Text("You will see I use icons or symbols to add clarity to what I'm
demonstrating. These come from Apple's new symbol font library which you can browse
using an app called 'SF Symbols'.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.layoutPriority(1)

// Use "systemName" when you want to use "SF Symbols"


Image(systemName: "hand.thumbsup.fill")
.font(.largeTitle) // Make the symbol larger

Image("SF Symbols") // Regular image from Assets.xcassets


}
.font(.title)
.edgesIgnoringSafeArea(.bottom) // Ignore the bottom screen border
}
}

Even though an Image view is used to initialize a symbol, you use the font modifier to change its size.
These symbols actually come from fonts. So use font modifiers to change them. There is a whole section
that covers this.

www.bigmountainstudio.com 38 Free Sample (Get the Full Version Here)


SwiftUI Basics

Layers
VStack(spacing: 40) {
Text("Layers")
.font(.largeTitle)

Text("The Basics")
.foregroundColor(.gray)

Text("With SwiftUI views, you can add layers on top (.overlay) and behind
(.background) the view.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.layoutPriority(1)

Image("yosemite") // Show an image from Assets.xcassets


.opacity(0.7) // Make image only 70% solid
.background(Color.red.opacity(0.3)) // Layer behind image
.background(Color.yellow.opacity(0.3)) // Layer behind red
.background(Color.blue.opacity(0.3)) // Layer behind yellow
.overlay(Text("Yosemite")) // Layer on top of image

Image("Layers")
}
.font(.title)

I use layers (background and overlay) early in the book so I want to make sure you understand this
concept.

Both of these modifiers are explained in detail in their own sections.

www.bigmountainstudio.com 39 Free Sample (Get the Full Version Here)


SwiftUI Basics

Short Introduction to Shapes


VStack(spacing: 20) {
Text("Shapes")
.font(.largeTitle)
Text("Short Introduction")
.foregroundColor(.gray)
Text("I'll make shapes, give them color and put them behind other views just for
decoration.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.layoutPriority(3) // Highest priority

Text("This text has a rounded rectangle behind it")


.foregroundColor(Color.white)
.padding()
.background(
RoundedRectangle(cornerRadius: 20) // Create the shape
.foregroundColor(Color.blue)) // Make shape blue
.padding()
.layoutPriority(2) // You need to play with priority order to get it right
sometimes

Text("But sometimes I'll use color and a corner radius:")

Text("This text has a color with a corner radius")


.foregroundColor(Color.white)
.padding()
.background(Color.blue) // Use a color as the background layer
.cornerRadius(20) // Rounded corners on whole text view
.layoutPriority(1)
}
.font(.title)

www.bigmountainstudio.com 40 Free Sample (Get the Full Version Here)


Layout Behavior

In SwiftUI, you may wonder why some views layout differently than others. You can observe two behaviors when it comes
to the size and layout of views:
1. Some views pull in to be as small as possible to fit their content. (I will refer to these as “pull-in” views.)
2. Some views push out to fill all available space. (I will refer to these as “push-out” views.)
Knowing these two behaviors can help you predict how to layout views to create the screen you want.

www.bigmountainstudio.com 41
Layout Behavior

Some Views Pull In


struct ViewSizes_Pull_In: View {
var body: some View {
VStack(spacing: 20) {
Text("Layout Behavior").font(.largeTitle)
Text("Views that Pull In").foregroundColor(.gray)
Text("Some views minimize their frame size so it is only as big as the
content within it.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.purple)
.foregroundColor(Color.white)
.layoutPriority(1)

Image(systemName: "arrow.down.to.line.alt")

HStack { // Order views horizontally


Image(systemName: "arrow.right.to.line.alt")
Text("Text views pull in")
Image(systemName: "arrow.left.to.line.alt")
}

Image(systemName: "arrow.up.to.line.alt")

Text("Pull-In views tend to center themselves within their parent container


view.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.purple)
.foregroundColor(Color.white)
.layoutPriority(1)
}.font(.title)
}
}

www.bigmountainstudio.com 42 Free Sample (Get the Full Version Here)


Layout Behavior

Some Views Push Out


struct ViewSizes_Push_Out: View {
var body: some View {
VStack(spacing: 20) {
Text("Layout Behavior")
Text("Views that Push Out")
.font(.title).foregroundColor(.gray)
Text("Some views will push out to fill up all available space within their
parent.")
.frame(maxWidth: .infinity).padding().font(.title)
.background(Color.purple).layoutPriority(1)

Color.purple
// Add 5 layers on top of the color view
.overlay(
Image(systemName: "arrow.up.left")
.padding() // Add spacing around the symbol
, alignment: .topLeading) // Align within the layer
.overlay(
Image(systemName: "arrow.up.right")
.padding(), alignment: .topTrailing)
.overlay(
Image(systemName: "arrow.down.left")
.padding(), alignment: .bottomLeading)
.overlay(
Image(systemName: "arrow.down.right")
.padding(), alignment: .bottomTrailing)
.overlay(Text("Colors are Push-Out views"))
}.font(.largeTitle) // Make all text and symbols larger
}
}

www.bigmountainstudio.com 43 Free Sample (Get the Full Version Here)


SEE YOUR WORK

44
Preview Options

As you practice these examples, you might want to see your SwiftUI working on different devices in different modes,
including light or dark mode or with different accessibility settings.

You can do all of this without even having to launch the Simulator. When using SwiftUI, you get a preview canvas that will
show you how your views will render.

(Note: You will need to be running Xcode 11 or later on macOS Catalina or later.)

www.bigmountainstudio.com 45
Preview Options

The Canvas - What is it?

The canvas is the area next to the code that shows


you a preview of how your SwiftUI will look. You
might also hear this called the “Preview” or
“Preview Canvas”.

If you do not see this pane, click on the Editor


Options button on the top right of your code
window and click Canvas:

www.bigmountainstudio.com 46 Free Sample (Get the Full Version Here)


Preview Options

Introduction
struct Previews_Intro: View {
var body: some View {
VStack(spacing: 20) {
Text("Previews")
.font(.largeTitle)

Text("Introduction")
.foregroundColor(.gray)

Text("Xcode looks for a struct that conforms to the PreviewProvider protocol


and accesses its previews property to display a view on the Canvas.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.red)
.layoutPriority(1)
.foregroundColor(.white)

}.font(.title)
}
}

// Xcode looks for PreviewProvider struct


struct Previews_Intro_Previews: PreviewProvider {
// It will access this property to get a view to show in the Canvas (if the Canvas
is shown)
static var previews: some View {
// Instantiate and return your view inside this property to see a preview of it
Previews_Intro()
}
}

www.bigmountainstudio.com 47 Free Sample (Get the Full Version Here)


Preview Options

Dark Mode
struct Preview_DarkMode: View {
var body: some View {
VStack(spacing: 20) {
Text("Previews").font(.largeTitle)
Text("Dark Mode").foregroundColor(.gray)
Text("By default, your preview will show in light mode. To see it in dark
mode, you can use the environment modifier.")
...

}.font(.title)
}
}

struct Preview_DarkMode_Previews: PreviewProvider {


static var previews: some View {
Preview_DarkMode()
// Use the environment function and pass in a property key path and a value
to set that property to.
.environment(\EnvironmentValues.colorScheme, ColorScheme.dark)
}
}

What is that backslash for?


This is a KeyPath symbol used to access the colorScheme property to assign a new value to it without
having to reference the type that contains that property. There may be different environment types for
macOS, iOS, tvOS, etc. So you are just setting the property and letting Xcode handle the rest for you.

www.bigmountainstudio.com 48 Free Sample (Get the Full Version Here)


Preview Options

Light & Dark Modes Together


struct Preview_LightAndDarkMode: View {
var body: some View {
VStack(spacing: 20) {
Text("Previews").font(.largeTitle)
Text("Light & Dark Modes Together").foregroundColor(.gray)
Text("Group your views to preview more than one at a time.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.red)
.foregroundColor(.white)

}.font(.title)
}
}

struct Preview_LightAndDarkMode_Previews: PreviewProvider {


static var previews: some View {
// Just use a Group container to instantiate your views in
Group {
Preview_LightAndDarkMode() // Light Mode
Preview_LightAndDarkMode()
.environment(\.colorScheme, .dark)
}
}
}

Dark mode isn’t showing up for me.


This has been a problem and it may not work for you. It hasn’t worked for me.

www.bigmountainstudio.com 49 Free Sample (Get the Full Version Here)


Preview Options

Changing Devices
struct Previews_Devices: View {
var body: some View {
VStack(spacing: 20) {
Text("Previews").font(.largeTitle)
Text("Change Devices").foregroundColor(.gray)
Text("By default, your canvas will use the simulator you
currently have selected (upper left in Xcode). You can preview a
different device using the previewDevice modifier.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.red)
.foregroundColor(.white)
}.font(.title)
}
}

struct Previews_Devices_Previews: PreviewProvider {


static var previews: some View {
Previews_Devices()
.previewDevice(PreviewDevice(rawValue: "iPad Pro (9.7-
inch)"))
}
}

How do I know what to type for a device?


Just look at your list of simulators and type in exactly as you see them displayed in
the list.

www.bigmountainstudio.com 50
Preview Options

Size Category
struct Preview_SizeCategory: View {
var body: some View {
VStack(spacing: 20) {
...
Text("For testing accessibility text size, set the sizeCategory property.")
...
}.font(.title)
}
}

struct Preview_SizeCategory_Previews: PreviewProvider {


static var previews: some View {
Preview_SizeCategory()
.environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
/*
Options:
case accessibilityExtraExtraExtraLarge
case accessibilityExtraExtraLarge
case accessibilityExtraLarge
case accessibilityLarge
case accessibilityMedium
case extraExtraExtraLarge
case extraExtraLarge
case extraLarge
case extraSmall
case large
case medium
case small
*/
}
}

www.bigmountainstudio.com 51 Free Sample (Get the Full Version Here)


Preview Options

Landscape

Text("You currently cannot rotate a previewed device. But one option is to set a fixed width and height for your preview.")

struct Preview_Landscape_Previews: PreviewProvider {


static var previews: some View {
Preview_Landscape()
.previewLayout(PreviewLayout.fixed(width: 896, height: 414))
}
}

I would run your project and rotate your simulator for more accurate results.

www.bigmountainstudio.com 52
Preview Options

Environment Overrides

If you prefer to see your work in the Simulator then you can access many of the options
mentioned through the Environment Overrides options.

This button will show up when you run your app in the debugging toolbar at the bottom of
Xcode.

www.bigmountainstudio.com 53 Free Sample (Get the Full Version Here)


LAYOUT VIEWS

54
VStack

VStack stands for “Vertical Stack”. It is a pull-in container view in which you pass in up to ten views and it will compose them
one below the next, going down the screen.

www.bigmountainstudio.com 55
VStack

Introduction
VStack(spacing: 20) {
Text("VStack")
.font(.largeTitle)
Text("Introduction")
.font(.title)
.foregroundColor(.gray)
Text("VStacks are views that contain other views")
.frame(maxWidth: .infinity, minHeight: 70)
.padding().font(.title)
.background(Color.blue)
.foregroundColor(.white)
Text("The containing views are stacked vertically")
.font(.title)
VStack {
Text("VStack inside another VStack")
.layoutPriority(1) // Give spacing priority to this view first (prevents text
wrapping)
Divider()
Text("This can be handy. Why?")
Divider()
Text("More than 10 views creates an error.")
}
.font(.title) // Apply this font to all text within parent
.layoutPriority(1)) // 2nd spacing priority (prevents text wrapping)
.padding()
.foregroundColor(Color.white)
.background(
// Use a blue rectangle as the background
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.blue))
.padding()
}

www.bigmountainstudio.com 56 Free Sample (Get the Full Version Here)


VStack

Spacing
VStack(spacing: 80) {
Text("VStack")
.font(.largeTitle)

Text("Spacing")
.font(.title)
.foregroundColor(.gray)

Text("The VStack initializer allows you to set the spacing between all the views inside the
VStack")
.frame(maxWidth: .infinity)
.padding().layoutPriority(1)
.background(Color.blue).font(.title)
.foregroundColor(.white)

Image(systemName: "arrow.up.and.down.circle.fill")
.font(.largeTitle)

Text("The spacing here between all of these views is 80")


.font(.title)
}

www.bigmountainstudio.com 57 Free Sample (Get the Full Version Here)


VStack

Alignment
VStack(spacing: 20) {
Text("VStack")
.font(.largeTitle)
Text("Alignment")
.font(.title)
.foregroundColor(.gray)
Text("By default, views in a VStack are center aligned.")
...

VStack(alignment: .leading, spacing: 40) {


Text("Leading Alignment")
.font(.title)
Divider() // Creates a thin line (Push-out view)
Image(systemName: "arrow.left")
}
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(.blue))
.padding()

VStack(alignment: .trailing, spacing: 40) {


Text("Trailing Alignment")
.font(.title)
Divider()
Image(systemName: "arrow.right")
}
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(.blue))
.padding()
}

www.bigmountainstudio.com 58 Free Sample (Get the Full Version Here)


HStack

HStack stands for “Horizontal Stack”. It is a pull-in container view in which you pass in up to ten views and it will compose
them side-by-side.

www.bigmountainstudio.com 59
HStack

Introduction
VStack(spacing: 20) {
Text("HStack").font(.largeTitle)
Text("Introduction")
.font(.title).foregroundColor(.gray)
Text("HStacks are views that contain other views laid out horizontally.")
...
HStack {
Text("Leading")
Text("Middle")
Text("Trailing")
}
.padding()
.border(Color.orange) // Create a 2 point border using the color specified

HStack(spacing: 10) {
Image(systemName: "1.circle")
Image(systemName: "2.circle")
Image(systemName: "3.circle")
}.padding()

HStack(spacing: 20) {
Image(systemName: "a.circle.fill")
Image(systemName: "b.circle.fill")
Image(systemName: "c.circle.fill")
Image(systemName: "d.circle.fill")
Image(systemName: "e.circle.fill")
}
.font(.largeTitle).padding()
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(.orange))
}

www.bigmountainstudio.com 60 Free Sample (Get the Full Version Here)


HStack

Spacing
VStack(spacing: 40) {
Text("HStack")
.font(.largeTitle)

Text("Spacing")
.font(.title)
.foregroundColor(.gray)

Text("The HStack initializer allows you to set the spacing between all the views inside the
HStack")
.frame(maxWidth: .infinity)
.padding().layoutPriority(1)
.background(Color.orange).font(.title)
.foregroundColor(.black)

Text("Default Spacing")
.font(.title)
HStack {
Image(systemName: "1.circle")
Image(systemName: "2.circle")
Image(systemName: "3.circle")
}.font(.largeTitle)

Divider()

Text("Spacing: 100")
.font(.title)
HStack(spacing: 100) {
Image(systemName: "1.circle")
Image(systemName: "2.circle")
Image(systemName: "3.circle")
}.font(.largeTitle)
}

www.bigmountainstudio.com 61 Free Sample (Get the Full Version Here)


HStack

Alignment
Text("By default, views within an HStack are vertically aligned in the center.")
...
HStack {
Rectangle().foregroundColor(.orange).frame(width: 25)
Text("Leading")
Spacer()
Text("Center")
Spacer()
Text("Trailing")
.padding(.trailing)
}
.border(Color.orange)
HStack(alignment: .top) {
Rectangle().foregroundColor(.orange).frame(width: 25)
Text("Leading")
Spacer()
Text("Top")
Spacer()
Text("Trailing")
.padding(.trailing)
}
.border(Color.orange)
HStack(alignment: .bottom) {
Rectangle().foregroundColor(.orange).frame(width: 25)
Text("Leading")
Spacer()
Text("Bottom")
Spacer()
Text("Trailing")
.padding(.trailing)
}
.border(Color.orange)

www.bigmountainstudio.com 62 Free Sample (Get the Full Version Here)


HStack

Layout Priority

When using a horizontal stack with text views within it, there’s a chance that text might truncate if
you are not allowing them to wrap. In this case, you can prioritize which one will truncate last with
layout priority. The default value is 0. The higher the number, the higher the priority to have
enough space to not be truncated.

HStack {
Text("SwiftUI")
.font(.largeTitle).lineLimit(1) // Don't let text wrap
Image("SwiftUI")
.resizable()
.frame(width: 80, height: 80)
Text("Brings Balance")
.font(.largeTitle)
.layoutPriority(1) // Truncate last
}
.padding([.horizontal])
Divider()
HStack {
Text("SwiftUI")
.font(.largeTitle)
.layoutPriority(1) // Truncate last
Image("SwiftUI")
.resizable()
.frame(width: 80, height: 80)
Text("Brings Balance")
.font(.largeTitle).lineLimit(1) // Don't let text wrap
}
.padding(.horizontal)

www.bigmountainstudio.com 63 Free Sample (Get the Full Version Here)


Depth (Z) Stack

A ZStack is a push-out container view. It is a view that overlays its child views on top of each other. (“Z” represents the Z-axis
which is depth-based in a 3D space.)

You learned earlier about creating layers with the background and overlay modifiers. ZStack is another way to create layers
with views that control their own sizing and spacing.

The ZStack is a pull-in container view. You may think it is a push-out view because of the first example but it’s actually the
color that is pushing out

www.bigmountainstudio.com 64
ZStack

Introduction
ZStack {
// LAYER 1: Furthest back
Color.gray // Yes, Color is a view!

// LAYER 2: This VStack is on top.


VStack(spacing: 20) {
Text("ZStack")
.font(.largeTitle)

Text("Introduction")
.foregroundColor(.white)

Text("ZStacks are great for setting a background color.")


.frame(maxWidth: .infinity)
.padding()
.background(Color.green)

Text("But notice the Color stops at the Safe Areas (white areas on top and bottom).")
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
}
.font(.title)
}

www.bigmountainstudio.com 65 Free Sample (Get the Full Version Here)


ZStack

Ignore Safe Area


ZStack {
Color.gray

VStack(spacing: 20) {
Text("ZStack")
.font(.largeTitle)

Text("Edges Ignoring Safe Area")


.foregroundColor(.white)

Text("Ignoring the Safe Areas will extend a view to fill the whole scene.")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.white)
.background(Color.green)

}
.font(.title)
}
.edgesIgnoringSafeArea(.all) // Ignore the safe areas

www.bigmountainstudio.com 66 Free Sample (Get the Full Version Here)


ZStack

Background Problem
struct ZStack_BackgroundColor_Problem: View {
var body: some View {
ZStack {
Color.gray

VStack(spacing: 20) {
Text("ZStack") // This view is under the notch
.font(.largeTitle)

Text("Edges Ignoring Safe Area")


.foregroundColor(.white)

Text("Having the ZStack edges ignoring the safe areas might be a mistake if you
don't want other layers' edges to also ignore the safe areas. You notice that the top Text view
is completely under the notch.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)

Spacer() // Added a spacer to push the views up.


}
.font(.title)
}
.edgesIgnoringSafeArea(.all)
}
}

www.bigmountainstudio.com 67 Free Sample (Get the Full Version Here)


ZStack

Background Solution
struct ZStack_BackgroundColor_Solution: View {
var body: some View {
ZStack {
Color.gray
.edgesIgnoringSafeArea(.all) // Have JUST the color ignore the safe areas, not
the VStack.

VStack(spacing: 20) {
Text("ZStack")
.font(.largeTitle)

Text("Color Ignores Safe Area Edges")


.foregroundColor(.white)

Text("To solve the problem, you want just the color (bottom layer) to ignore the
safe area edges and fill the screen. Other layers above it will respect the safe areas.")
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
Spacer()
}
.font(.title)
}
}
}

www.bigmountainstudio.com 68 Free Sample (Get the Full Version Here)


ZStack

Layering & Aligning


VStack {
Text("ZStack")
.font(.largeTitle)

Text("Layering & Aligning")


.font(.title).foregroundColor(.gray)

Text("ZStacks are great for layering views. For example, putting text on top of an image.
You can align all the subviews within the ZStack.")
.frame(maxWidth: .infinity, minHeight: 171) // Force the height. Can increase but not go
below 171.
.padding().foregroundColor(.white)
.background(Color.green).font(.title)

ZStack(alignment: .bottomTrailing) {
Image("yosemite_large")

Rectangle()
.foregroundColor(transparentWhite)
.frame(width: 390, height: 50)

Text("Yosemite National Park")


.font(.title)
.padding(8) // Override the default padding with 8 points
}
Text("View Hierarchy")
.font(.title).foregroundColor(.gray)

Image("yosemite_layers")
}

www.bigmountainstudio.com 69 Free Sample (Get the Full Version Here)


Spacer

You may notice that when you add new pull-in views, such as Text views, they appear in the center of the screen. You can
use the Spacer to push these views apart, away from the center of the screen.

www.bigmountainstudio.com 70
Spacer

Introduction
VStack {
Text("Spacer")
.font(.largeTitle)

Text("Introduction")
.foregroundColor(.gray)

Text("Spacers push things away either vertically or horizontally")


...

Image(systemName: "arrow.up.circle.fill")

Spacer()

Image(systemName: "arrow.down.circle.fill")

HStack {
Text("Horizontal Spacer")

Image(systemName: "arrow.left.circle.fill")

Spacer()

Image(systemName: "arrow.right.circle.fill")
}
.padding(.horizontal)

Color.yellow
.frame(maxHeight: 50) // Height can decrease but not go higher than 50
}
.font(.title) // Apply this font to every view within the VStack

www.bigmountainstudio.com 71 Free Sample (Get the Full Version Here)


Spacer

Evenly Spaced
Text("Use Spacer to evenly space views horizontally so they look good on any
device.")
...
Text("After")
...
HStack {
Spacer()

VStack(alignment: .leading) {
Text("Names")
.font(.largeTitle)
.underline()
Text("Chase")
Text("Rodrigo")
Text("Mark")
Text("Evans")
}.layoutPriority(1)

Spacer()

VStack(alignment: .leading) {
Text("Color")
.font(.largeTitle)
.underline()
Text("Red")
Text("Orange")
Text("Green")
Text("Blue")
}.layoutPriority(1)

Spacer()
}

www.bigmountainstudio.com 72 Free Sample (Get the Full Version Here)


Spacer

Minimum Length
VStack(spacing: 10) {
Text("Spacer")
.font(.largeTitle)
Text("Minimum Length")
.font(.title)
.foregroundColor(.gray)
Text("You can set a minimum space to exist between views using the minLength modifier on the
Spacer.")
...
Text("No minLength set (system default is used)")
.bold()
HStack {
Image("yosemite")
Spacer()
Text("This is Yosemite National Park").lineLimit(1)
}.padding()

Text("minLength = 0")
.bold()
HStack {
Image("yosemite")
Spacer(minLength: 0)
Text("This is Yosemite National Park").lineLimit(1)
}.padding()

Text("minLength = 20")
.bold()
HStack {
Image("yosemite")
Spacer(minLength: 20)
Text("This is Yosemite National Park").lineLimit(1)
}.padding()
}

www.bigmountainstudio.com 73 Free Sample (Get the Full Version Here)


GeometryReader

It is difficult, if not impossible, to get the size of a view. This is where the GeometryReader comes in.

The GeometryReader is similar to a push-out container view in that you can add child views to. It will allow you to inspect
and use properties that can help with positioning other views within it. You can access properties like height, width and safe
area insets which can help you dynamically set the sizes of views within it so they look good on any size device.

www.bigmountainstudio.com 74
Geometry Reader

Introduction
struct GeometryReader_Intro : View {
var body: some View {
VStack(spacing: 20) {
Text("GeometryReader")
.font(.largeTitle)

Text("Introduction")
.font(.title)
.foregroundColor(.gray)

Text("GeometryReader is a container view that pushes out to fill up all available


space. You use it to help with positioning items within it.")
.font(.title)
.padding()
.layoutPriority(1)

GeometryReader {_ in
Text("Views center automatically inside the GeometryReader")
.font(.title)
}
.foregroundColor(.white)
.background(Color.pink)

}
}
}

www.bigmountainstudio.com 75 Free Sample (Get the Full Version Here)


Geometry Reader

Positioning
struct GeometryReader_Positioning: View {
var body: some View {
VStack(spacing: 20) {
Text("GeometryReader").font(.largeTitle)
Text("Positioning").font(.title).foregroundColor(.gray)
Text("Use the geometry reader's variable to help position child views at different
locations within the geometry's view instead of it being in the center.")
.font(.title)
.layoutPriority(1)
.padding()

GeometryReader { geometry in
Text("Upper Left")
.font(.title)
.position(x: geometry.size.width/5,
y: geometry.size.height/10)

Text("Lower Right")
.font(.title)
.position(x: geometry.size.width - 90,
y: geometry.size.height - 40)
}
.background(Color.pink)
.foregroundColor(.white)

Text("Note: The position modifier uses the view's center point when setting the X
and Y parameters.")
.font(.title)
}
}
}

www.bigmountainstudio.com 76 Free Sample (Get the Full Version Here)


Geometry Reader

Getting Size
VStack(spacing: 10) {
Text("GeometryReader")
.font(.largeTitle)

Text("Getting Size")
.foregroundColor(.gray)

Text("Use the geometry reader when you need to get the height and/or width of a space.")
.padding()

GeometryReader { geometry in
VStack(spacing: 10) {
Text("Width: \(geometry.size.width)")
Text("Height: \(geometry.size.height)")
}
.foregroundColor(.white)
}
.background(Color.pink)

GeometryReader { geometry in
VStack(spacing: 10) {
Text("Width: \(geometry.size.width)")
Text("Height: \(geometry.size.height)")
}
.foregroundColor(.white)
}
.background(Color.pink)
.padding(30)
}
.font(.title)

www.bigmountainstudio.com 77 Free Sample (Get the Full Version Here)


Geometry Reader

Getting Coordinates
VStack(spacing: 10) {
Text("GeometryReader").font(.largeTitle)
Text("Getting Coordinates").foregroundColor(.gray)
Text("Getting the coordinates (x, y) of a geometry view is little different. Take a look at
this example:")
.layoutPriority(1)

GeometryReader { geometry in
VStack(spacing: 10) {
Text("X: \(geometry.frame(in: CoordinateSpace.local).origin.x)")
Text("Y: \(geometry.frame(in: CoordinateSpace.local).origin.y)")
}
.foregroundColor(.white)
}
.background(Color.pink)

Text("The local coordinate space will always give you zeros.")


Text("You need to look globally to get the coordinates inside the current view:")
GeometryReader { geometry in
VStack(spacing: 10) {
Text("X: \(geometry.frame(in: .global).origin.x)")
Text("Y: \(geometry.frame(in: .global).origin.y)")
}
.foregroundColor(.white)
}
.background(Color.pink)
.frame(height: 200)
}
.font(.title)
.padding(.horizontal)

www.bigmountainstudio.com 78 Free Sample (Get the Full Version Here)


Geometry Reader

Min Mid Max Coordinates


struct GeometryReader_MinMidMax: View {
var body: some View {
VStack(spacing: 20) {
Text("GeometryReader")
.font(.largeTitle)

Text("Min Mid Max")


.foregroundColor(.gray)

Text("You can also get the minimum (min), middle (mid), and maximum
(max) X and Y coordinate from the geometry reader.")
.layoutPriority(2)

GeometryReader { geometry in
VStack(alignment: .leading, spacing: 20) {
Text("Local Coordinate Space")
HStack(spacing: 10) {
// I'm converting to Int just so we don't have so many
zeros
Text("minX: \(Int(geometry.frame(in: .local).minX))")
Spacer()
Text("midX: \(Int(geometry.frame(in: .local).midX))")
Spacer()
Text("maxX: \(Int(geometry.frame(in: .local).maxX))")
}
Text("Global Coordinate Space")
HStack(spacing: 10) {
// I'm converting to Int just so we don't have so many
zeros
Text("minX: \(Int(geometry.frame(in: .global).minX))")
Spacer()

www.bigmountainstudio.com 79 Free Sample (Get the Full Version Here)


Geometry Reader

Min Mid Max Coordinates Continued


Text("midX: \(Int(geometry.frame(in: .global).midX))")
Spacer()
Text("maxX: \(Int(geometry.frame(in: .global).maxX))")
}
}.padding(.horizontal)
}
.frame(height: 200)
.foregroundColor(.white)
.background(Color.pink)

HStack {
GeometryReader { geometry in
VStack(spacing: 10) {
Text("minY: \(Int(geometry.frame(in: .global).minY))")
Spacer()
Text("midY: \(Int(geometry.frame(in: .global).midY))")
Spacer()
Text("maxY: \(Int(geometry.frame(in: .global).maxY))")
}.padding(.vertical)
}
.foregroundColor(.white)
.background(Color.pink)

Image("MinMidMax")
.resizable()
.aspectRatio(contentMode: .fit)
}.layoutPriority(1) // Layout this HStack BEFORE the Geometry reader above it.
}
.font(.title)
.padding()
}
}

Notice how the min, mid and max values change as


the geometry reader adapts to different device sizes.

www.bigmountainstudio.com 80 Free Sample (Get the Full Version Here)


Geometry Reader

Safe Area Insets

Text("GeometryReader can also tell you the safe area insets it has.")
...

GeometryReader { geometry in
VStack {
Text("geometry.safeAreaInsets.leading: \(geometry.safeAreaInsets.leading)")
Text("geometry.safeAreaInsets.trailing: \(geometry.safeAreaInsets.trailing)")
Text("geometry.safeAreaInsets.top: \(geometry.safeAreaInsets.top)")
Text("geometry.safeAreaInsets.bottom: \(geometry.safeAreaInsets.bottom)")
}
}
.font(.title)
.background(Color.purple)

www.bigmountainstudio.com 81 Free Sample (Get the Full Version Here)


CONTROL VIEWS

82
Button

The Button is a pull-in view with a wide range of composition and customization options to be presented to the user. The
button can be just text, just an image or both combined.

www.bigmountainstudio.com 83
Button

Introduction
VStack(spacing: 20) {
Text("Button")
.font(.largeTitle)
Text("Introduction")
.font(.title).foregroundColor(.gray)
Text("If you just want to show the default text style in a button then you can pass in a
string as the first parameter")
...

Button("Default Button Style") {


// Your code here
}

Text("You can customize the text shown for a button")


...
Button(action: {
// Your code here
}) {
Text("Headline Font")
.font(.headline)
}
Divider()
Button(action: {}) {
Text("Foreground Color")
.foregroundColor(Color.red)
}
Divider()
Button(action: {}) {
Text("Thin Font Weight")
.fontWeight(.thin)
} For more text customization options, see
}
the chapter on Text.

www.bigmountainstudio.com 84 Free Sample (Get the Full Version Here)


Button

Text Composition
Text("Button")
.font(.largeTitle)

Text("Text Composition")
.font(.title)
.foregroundColor(.gray)

Text("You can add more than one text view to a button. By default they are composed within a
VStack.")
.padding().frame(maxWidth: .infinity)
.background(Color.purple).layoutPriority(2)
.foregroundColor(.white).font(.title)

Button(action: {}, label: {


Text("New User")
.font(.title)
Text("(Register Here)")
})

Text("Using an HStack")
.padding().frame(maxWidth: .infinity)
.background(Color.purple).layoutPriority(1)
.foregroundColor(.white).font(.title)

Button(action: {}, label: {


HStack {
Text("Forgot Password?")
Text("Tap to Recover")
.foregroundColor(.orange)
}.font(.title)
})

www.bigmountainstudio.com 85 Free Sample (Get the Full Version Here)


Button

With Backgrounds
Text("Button").font(.largeTitle)
Text("With Backgrounds").font(.title).foregroundColor(.gray)
Text("As with most views, we can also customize the background and add a shadow.")
.padding().frame(maxWidth: .infinity)
.background(Color.purple).layoutPriority(1)
.foregroundColor(.white).font(.title)

Button(action: {}) {
Text("Solid Button")
.padding()
.foregroundColor(Color.white)
.background(Color.purple)
.cornerRadius(8)
}
Button(action: {}) {
Text("Button With Shadow")
.padding(12)
.foregroundColor(Color.white)
.background(Color.purple)
.cornerRadius(8)
}
.shadow(color: Color.purple, radius: 20, y: 5) // See more info in section on Shadows
Button(action: {}) {
Text("Button With Rounded Ends")
.padding(EdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20))
.foregroundColor(Color.white)
.background(Color.purple)
.cornerRadius(.infinity) // Infinity will always give you the perfect corner no matter
the size of the view.
}

www.bigmountainstudio.com 86 Free Sample (Get the Full Version Here)


Button

With Borders
Text("Button").font(.largeTitle)
Text("With Borders").font(.title).foregroundColor(.gray)
Text("Applying borders can add a nice effect to your buttons. Here are some options.")
.padding().frame(maxWidth: .infinity)
.background(Color.purple).layoutPriority(1)
.foregroundColor(.white).font(.title)

Button(action: {}) {
Text("Square Border Button")
.padding()
.border(Color.purple)
}
Button(action: {}) {
Text("Rounded Border Button")
.padding()
.border(Color.purple)
.cornerRadius(10)
}
Text("Look what happened when I tried to add a corner radius to the border. It is clipping the
corners. Here is a different way you can accomplish this:")
...

Button(action: {}) {
Text("Border Button")
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.purple, lineWidth: 2)
)
}

www.bigmountainstudio.com 87 Free Sample (Get the Full Version Here)


Button

With SF Symbols
Button(action: {}) {
HStack{
Text("Button With Symbol")
.padding(.horizontal)
Image(systemName: "gift.fill")
}.padding()
}
.foregroundColor(Color.white)
.background(Color.purple)
.cornerRadius(8)

Button(action: {}) {
HStack{
Image(systemName: "magnifyingglass")
Text("Search")
.padding(.horizontal)
}.padding()
}
.foregroundColor(Color.white)
.background(Color.purple)
.cornerRadius(8)

Button(action: {}) {
Image(systemName: "video.fill")
Text("Record")
.padding(.horizontal) For even more ways to customize buttons,
}
.padding() see the chapter on Paints where you can
.foregroundColor(Color.white) learn how to apply the 3 different gradients
.background(Color.purple)
to them.
.cornerRadius(.infinity)

www.bigmountainstudio.com 88 Free Sample (Get the Full Version Here)


Button

With Images
struct Button_WithPhotos: View {
var body: some View {
VStack(spacing: 20) {
Text("Button").font(.largeTitle)
Text("With Images")
.font(.title).foregroundColor(.gray)
Text("Buttons work fine with the SF Symbols. But what if you wanted to use a photo?
Look what happens:")
...

Button(action: {}) {
Image("yosemite")
.cornerRadius(40)
}
Text("This is because of the image rendering mode is set to \"template\" by default.
This means all non-transparent areas will use the accent color. You need to change the rendering
mode to fix this.")
.padding().frame(maxWidth: .infinity)
.background(Color.purple).layoutPriority(1)
.font(.title)

Button(action: {}) {
Image("yosemite")
// Change rendering mode to original
.renderingMode(.original)
.cornerRadius(40)
}
}
}
}

www.bigmountainstudio.com 89 Free Sample (Get the Full Version Here)


Button

Floating Action Button


ZStack {
VStack(spacing: 40) {
Text("Button")
.font(.largeTitle)

Text("Floating")
.font(.title).foregroundColor(.gray)

Text("You can also create floating buttons by using a ZStack so the button on the top
layer, over everything else")
...
Spacer()
}

VStack {
Spacer()
HStack {
Spacer()
Button(action: {}) {
Image(systemName: "plus")
.font(.title)
}
.padding(20)
.foregroundColor(Color.white)
.background(Color.orange)
.cornerRadius(.infinity)
}
.padding(.trailing, 30) // Add 30 points on the trailing side of the button
}
}

See the section on the Overlay modifier in


the Layout Modifiers chapter for more
ways to accomplish the same thing.

www.bigmountainstudio.com 90 Free Sample (Get the Full Version Here)


DatePicker

In order to get or set a value for the DatePicker, you need to bind it to a variable. This variable is then passed into the
DatePicker’s initializer. Then, all you need to do is change the value of the bound variable to set the date or time you want
to show in the DatePicker. Or read the bound variable’s value to see which date or time is currently selected.

This is a pull-in view.

www.bigmountainstudio.com 91
DatePicker

Introduction
struct DatePicker_Intro : View {
// The date picker will use this variable to get and set its value
@State private var nextFullMoonDate = Date()

var body: some View {


VStack(spacing: 20) {
Text("DatePicker").font(.largeTitle)
Text("Introduction").foregroundColor(.gray)
Text("Bind a date variable to get and set the date in the date picker")
.padding().frame(maxWidth: .infinity)
.background(Color.yellow)

HStack {
Spacer()
Image(systemName: "moon.circle")
Spacer()
Circle().frame(width: 60, height: 60.0)
Spacer()
Image(systemName: "moon.circle.fill")
Spacer()
}
.foregroundColor(Color.yellow)

DatePicker("", selection: $nextFullMoonDate,


displayedComponents: .date)

Text("Notice the indent of the picker.")


.padding().frame(maxWidth: .infinity)
.background(Color.yellow)
}.font(.title)
}
}

www.bigmountainstudio.com 92 Free Sample (Get the Full Version Here)


DatePicker

Titles
struct DatePicker_Titles: View {
@State private var nextFullMoonDate = Date()

var body: some View {


VStack(spacing: 20) {
Text("DatePicker").font(.largeTitle)
Text("Titles").foregroundColor(.gray).font(.title)
Text("You can provide an optional title for pickers.")
.padding().frame(maxWidth: .infinity)
.background(Color.yellow)
.font(.title)

// The first parameter is a label or title for the picker


DatePicker("Date of next full moon", selection: $nextFullMoonDate,
displayedComponents: .date)
.padding(.horizontal)

Text("This doesn't really look good. So you might want to add your own title.")
.padding().frame(maxWidth: .infinity)
.background(Color.yellow)
.font(.title)
}
}
}

www.bigmountainstudio.com 93 Free Sample (Get the Full Version Here)


DatePicker

Your Own Title


struct DatePicker_YourOwnTitle: View {
@State private var nextFullMoonDate = Date()

var body: some View {


VStack(spacing: 20) {
Text("DatePicker").font(.largeTitle)
Text("Your Own Title").foregroundColor(.gray)
Text("Even when you add your own title, you still have the problem with the date
picker indenting.")
.padding()
.frame(maxWidth: .infinity)
.background(Color.yellow)

VStack(spacing: 0) {
Text("Date of next full moon")
.font(.title)
DatePicker("", selection: $nextFullMoonDate,
displayedComponents: .date)
}

Text("How can you prevent the indenting?")


.padding()
.frame(maxWidth: .infinity)
.background(Color.yellow)
}.font(.title)
}
}

www.bigmountainstudio.com 94 Free Sample (Get the Full Version Here)


DatePicker

Labels Hidden
struct DatePicker_LabelsHidden: View {
@State private var nextFullMoonDate = Date()

var body: some View {


VStack(spacing: 20) {
Text("DatePicker")
.font(.largeTitle)

Text("Labels Hidden")
.foregroundColor(.gray)

Text("You use the labels hidden modifier to let SwiftUI know not to render or
reserve space for the label (title).")
.padding()
.frame(maxWidth: .infinity)
.background(Color.yellow)

VStack(spacing: 0) {
Text("Date of next full moon")
DatePicker("Select a date for next full moon",
selection: $nextFullMoonDate,
displayedComponents: .date)
.labelsHidden() // Hide the label/title
}
}
.font(.title)
}
}

Note: Even though the label/title is not shown, I would still recommend having one because it will still
be used for accessibility purposes.

www.bigmountainstudio.com 95 Free Sample (Get the Full Version Here)


DatePicker

Displayed Components
struct DatePicker_DisplayedComponents: View {
@State private var justTime = Date()
@State private var theDateAndTime = Date()

var body: some View {


VStack(spacing: 9) {
Text("DatePicker")
.font(.largeTitle)
Text("Displayed Components")
.foregroundColor(.gray)

Text("Show date and time parts with displayedComponents.")


.frame(maxWidth: .infinity)
.padding().background(Color.yellow)
.foregroundColor(.black)

DatePicker("", selection: $justTime,


displayedComponents: .hourAndMinute)

Text("Combine date and time with an array.")


.padding().frame(maxWidth: .infinity)
.background(Color.yellow)
.foregroundColor(.black)

DatePicker("", selection: $theDateAndTime,


displayedComponents: [.date, .hourAndMinute])
}
.font(.title)
.labelsHidden() // Notice I can add this modifier to the parent to be applied to the
children
}
}

www.bigmountainstudio.com 96 Free Sample (Get the Full Version Here)


DatePicker

In Forms and Lists


struct DatePicker_DisplayedComponents: View {
@State private var justTime = Date()
@State private var theDateAndTime = Date()

var body: some View {


VStack(spacing: 9) {
Text("DatePicker")
.font(.largeTitle)
Text("Displayed Components")
.foregroundColor(.gray)

Text("Show date and time parts with displayedComponents.")


.frame(maxWidth: .infinity)
.padding().background(Color.yellow)
.foregroundColor(.black)

DatePicker("", selection: $justTime,


displayedComponents: .hourAndMinute)

Text("Combine date and time with an array.")


.padding().frame(maxWidth: .infinity)
.background(Color.yellow)
.foregroundColor(.black)

DatePicker("", selection: $theDateAndTime,


displayedComponents: [.date, .hourAndMinute])
}
.font(.title)
.labelsHidden() // Notice I can add this modifier to the parent to be applied to the
children
}
}
Labels work great when the date picker is used within a form.

www.bigmountainstudio.com 97 Free Sample (Get the Full Version Here)


DatePicker

From a Specific Date or Time


struct DatePicker_FromSpecificDateTime: View {
@State private var arrivalDate = Date()
let fromToday = Calendar.current.date(byAdding: .minute,
value: -1, to: Date())!
let mainColor = Color("AccentColorDark")

var body: some View {


VStack(spacing: 20) {
Text("DatePicker").font(.largeTitle)
Text("From Date or Time").foregroundColor(.gray)
Text("You can set a starting date or time with the date picker")
.frame(maxWidth: .infinity).padding()
.background(mainColor).foregroundColor(.white)
Image("banff").resizable()
.aspectRatio(contentMode: .fit)
Text("Arrival Date")
DatePicker("", selection: $arrivalDate, in: fromToday...,
displayedComponents: .date)
.padding(.horizontal, 28)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(mainColor))
.padding(.horizontal)
.labelsHidden()
}.font(.title)
}
}

www.bigmountainstudio.com 98 Free Sample (Get the Full Version Here)


DatePicker

To a Specific Date or Time


struct DatePicker_DateTimeTo: View {
@State private var arrivalDate = Date()
let mainColor = Color("LightPinkColor")

var body: some View {


VStack(spacing: 20) {
Text("DatePicker")
.font(.largeTitle)
Text("To Date or Time")
.foregroundColor(.gray)
Text("You can set a maximum date or time that you cannot go past with the date
picker")
.frame(maxWidth: .infinity)
.padding().background(mainColor)

Image("baby")
.resizable()
.aspectRatio(contentMode: .fit)

Text("Date and Time of Birth")

DatePicker("", selection: $arrivalDate, in: ...Date(),


displayedComponents: [.date, .hourAndMinute])
.background(
RoundedRectangle(cornerRadius: 20)
.stroke(mainColor, lineWidth: 2))
.padding(.horizontal)
.labelsHidden()
}
.font(.title)
}
}

www.bigmountainstudio.com 99 Free Sample (Get the Full Version Here)


DatePicker

With Minimum and Maximum Date Range


@State private var nextFullMoonDate = Date()

var withinNext30Days: ClosedRange<Date> {


let today = Calendar.current.date(byAdding: .minute, value: -1,
to: Date())!
let next30Days = Calendar.current.date(byAdding: .day, value: 30,
to: Date())!
return today...next30Days
}
var body: some View {
VStack(spacing: 30) {
Text("DatePicker").font(.largeTitle)
Text("Min and Max Date Range")
.foregroundColor(.gray)
HStack {
// Moon images
}
.foregroundColor(Color.yellow)

Text("Select date of next full moon").font(.title)


DatePicker("", selection: $nextFullMoonDate,
in: withinNext30Days,
displayedComponents: .date)
.padding(.horizontal, 28)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(.yellow))
.shadow(radius: 20, y: 20)
.labelsHidden()
Text("(Valid range only in the next 30 days)")
.padding(.vertical)
Spacer()
}.font(.title)
}

www.bigmountainstudio.com 100 Free Sample (Get the Full Version Here)


Form

The Form view is a great choice when you want to show settings, options, or get some user input. It is easy to set up and
customize as you will see on the following pages.

This is a push-out view.

www.bigmountainstudio.com 101
Form

Introduction
struct Form_Intro : View {
var body: some View {
Form {
Section {
Text("This is a Form!")
.font(.title)
Text("You can put any content in here")
Text("The cells with grow to fit the content")
Text("Remember, it's just views inside of views")
}

Section {
Text("Limitations")
.font(.title)
Text("There are built-in margins that are difficult to get around. Take a look
at the color below so you can see where the margins are:")
Color.purple
}

Section {
Text("Summary")
.font(.title)
Text("Pretty much what you see here is what you get.")
}
}
}
}

www.bigmountainstudio.com 102 Free Sample (Get the Full Version Here)


Form

Section Headers and Footers


struct Form_HeadersAndFooters : View {
var body: some View {
Form {
Section(header: Text("Section Header Text")) {
Text("You can add any view in a section header")
Text("Notice the default foreground color is gray")
}
Section(header: SectionTextAndImage(name: "People", image:
"person.2.square.stack.fill")) {
Text("Here's an example of a section header with image and text")
}
Section(header: Text(""), footer: Text("Total: $5,600.00").bold()) {
Text("Here is an example of a section footer")
}
}
}
}

struct SectionTextAndImage: View {


var name: String
var image: String
var body: some View {
HStack {
Image(systemName: image).padding(.trailing)
Text(name)
}
.padding()
.font(.title)
.foregroundColor(Color.purple)
}
}

www.bigmountainstudio.com 103 Free Sample (Get the Full Version Here)


Form

List Row Background


struct Form_ListRowBackground : View {
var body: some View {
Form {
Section(header: Text("Form").font(.largeTitle)) {
Text("List Row Background")
.foregroundColor(.gray)

Text("Forms and Lists allow you to set a background view with a function called
\"listRowBackground(view:)\".")
.fixedSize(horizontal: false, vertical: true)
// Using fixedSize is another way to get text not to truncate.
// See chapter on "Layout Modifiers" in full book.

Text("You can call this modifier function on just one row, like this.")
.listRowBackground(Color.purple)
.foregroundColor(.white)
}

Section(header: Text("Whole Section")


.font(.title).foregroundColor(.gray)) {
Text("Or you can set a view or color for a whole section.")

Image(systemName: "smiley.fill")
.frame(maxWidth: .infinity, alignment: .center)
.font(.largeTitle)

Text("Note, even though the color is set on the Section, the color of the
section header is not affected.")
.fixedSize(horizontal: false, vertical: true)
}
.foregroundColor(.white)
.listRowBackground(Color.purple)
}.font(.title)
}
}

www.bigmountainstudio.com 104 Free Sample (Get the Full Version Here)


Form

Background Images
Form {
Section(header: Text("Form")
.font(.largeTitle).bold()
.foregroundColor(.white)) {
Text("List Row Background")
.font(.title)
.foregroundColor(.gray)
Text("Images work a little differently as you can see here.")
Text("The image is actually set on a row on the second section.")
}

Section(header: Text("Images")
.font(.title)
.foregroundColor(.white)) {
Text("An image is set as a background for the row below. This works fine for rows,
but when you use an image on the section level, it is repeated for all rows.")
Text("The image is set on THIS row, but it extends past the bounds. It also hides
the row below this one and goes under the previous rows.")
.foregroundColor(.white)
.foregroundColor(.white)
.listRowBackground(Image("water")
.clipped()
.blur(radius: 3))
Text("This row cannot be seen.")
}
}

www.bigmountainstudio.com 105 Free Sample (Get the Full Version Here)


Form

List Row Inset


struct Form_ListRowInset : View {
var body: some View {
Form {
Section(header: Text("Form").font(.largeTitle)) {
Text("List Row Inset")
.font(.title)
.foregroundColor(.gray)
Text("Using this color, you can see where the default margins are:")
Color.purple
Text("You can use the List Row Inset modifier to adjust the margins:")
Color.purple
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
}
Section(header: Text("Row Inset Uses")
.font(.title).foregroundColor(.gray)) {
Text("Other possible uses could be for indenting")
Text("Indent Level 1")
.listRowInsets(EdgeInsets(top: 0, leading: 40, bottom: 0, trailing: 0))
Text("Indent Level 2")
.listRowInsets(EdgeInsets(top: 0, leading: 80, bottom: 0, trailing: 0))
Text("Or Vertical Alignment")
Text("Top")
.listRowInsets(EdgeInsets(top: -20, leading: 40, bottom: 0, trailing:
0))
Text("Bottom")
.listRowInsets(EdgeInsets(top: 20, leading: 40, bottom: 0, trailing: 0))
}
}
}
}

www.bigmountainstudio.com 106 Free Sample (Get the Full Version Here)


Form

With Controls
struct Form_WithControls : View {
@State private var isOn = true
@State private var textFieldData = "This is a text field"

var body: some View {


Form {
Section(header: SectionHeader(name: "Controls in a Form")) {
Text("This will give you an idea of how different controls are rendered in a
Form.")
}

Section {
Button(action: {}) { Text("Button") }
Toggle(isOn: $isOn) { Text("Toggle") }
Stepper(onIncrement: {}, onDecrement: {}) { Text("Stepper") }
TextField("", text: $textFieldData)
.textFieldStyle(RoundedBorderTextFieldStyle())
Image(systemName: "leaf.arrow.circlepath").font(.title)
Circle()
Text("Notice shapes are centered ☝ ")
TextField("", text: $textFieldData)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
}
}

struct SectionHeader: View {


var name: String

var body: some View {


Text(name)
.font(.largeTitle)
.foregroundColor(Color.gray)
}
}

www.bigmountainstudio.com 107 Free Sample (Get the Full Version Here)


List

Using a List view is the most efficient way of displaying vertically scrolling data. You can display data in a ScrollView, as you
will see later on, but it will not be as efficient in terms of memory or performance as the List view.

www.bigmountainstudio.com 108
List

With Static Views


struct List_WithStaticData: View {
var body: some View {
VStack(spacing: 20) {
Text("List").font(.largeTitle)
Text("Static Data").font(.title).foregroundColor(.gray)
Text("You can show static views or data within the List view. It does not have to be
bound with data. It gives you a scrollable view.")
.frame(maxWidth: .infinity)
.font(.title).padding()
.background(Color.orange)
.layoutPriority(1)
.foregroundColor(.black)

List {
Text("Line One")
Text("Line Two")
Text("Line Three")
Image("profile")
Button("Click Here", action: {})
.foregroundColor(.orange)
HStack {
Spacer()
Text("Centered Text")
Spacer()
}.padding()
}
.font(.title)
}
}
}

www.bigmountainstudio.com 109 Free Sample (Get the Full Version Here)


List

With Data
struct List_WithData : View {
var stringArray = ["This is the simplest List", "Evans", "Lemuel James Guerrero", "Mark",
"Durtschi", "Chase", "Adam", "Rodrigo", "Notice the automatic wrapping when the content is
larger"]

var body: some View {


List(stringArray, id: \.self) { string in
Text(string)
}
.font(.largeTitle) // Apply this font style to all items in the list
}
}

What is that .id parameter?


You use this parameter to tell the List how it can uniquely identify each row by which value. The List
needs to know this so it can compare rows by this value to perform different operations like reordering
and deleting rows for us.

In this scenario, we are using “self” to say, “Just use the value of the string itself to uniquely identify each
row.”

www.bigmountainstudio.com 110 Free Sample (Get the Full Version Here)


List

Grouped List Style


var data = ["Grouped List Style", "This list is using the group list style", "Evans", "Lemuel
James Guerrero", "Mark", "Durtschi", "Chase", "Adam", "Rodrigo"]

var body: some View {


List(data, id: \.self) { datum in
HStack {
Text(datum)
.font(datum == "Grouped List Style"
? Font.largeTitle : Font.body)

Spacer()

Image(systemName: datum == "Mark"


? "circle.fill" : "circle")
.foregroundColor(Color.green)
}
}
// Change the List Style to Grouped
.listStyle(GroupedListStyle())
}

Notice that with the grouped list style that the rows don’t continue past the last one.

One more thing to note is that inside the List you see an HStack used for the row. This is optional. By
default, the list will implicitly use an HStack for the row if one is not specified.

www.bigmountainstudio.com 111 Free Sample (Get the Full Version Here)


List

Custom Rows
struct List_CustomRows : View {
var data = ["Custom Rows!", "Evans", "Lemuel James Guerrero", "Mark", "Durtschi", "Chase",
"Adam", "Rodrigo"]

var body: some View {


List(data, id: \.self) { datum in
CustomRow(content: datum)
}
}
}

struct CustomRow: View {


var content: String

var body: some View {


HStack {
Image(systemName: "person.circle.fill")
Text(content)
Spacer()
}
.foregroundColor(content == "Custom Rows!" ? Color.green : Color.primary)
.font(.title)
.padding([.top, .bottom])
}
}

www.bigmountainstudio.com 112 Free Sample (Get the Full Version Here)


List

Delete Rows
struct List_Delete : View {
@State var data = ["Swipe to Delete", "Practice Coding", "Grocery shopping", "Get tickets",
"Clean house", "Do laundry", "Cook dinner", "Paint room"]

var body: some View {


List {
Section(header: Text("To Do").padding()) {
ForEach(data, id: \.self) { datum in
Text(datum).font(Font.system(size: 24)).padding()
}
.onDelete(perform: delete) // Enables swipe to delete
}
}
}

func delete(at indexes: IndexSet) {


if let first = indexes.first {
data.remove(at: first)
}
}
}

onDelete, onMove, onInsert

These three functions only work on views that implement the DynamicViewContent protocol. Currently,
the only view that conforms to the DynamicViewContent protocol is the ForEach view. So these
functions are only available on a ForEach view, not a List view.

www.bigmountainstudio.com 113 Free Sample (Get the Full Version Here)


List

Move Rows
struct List_MoveRow : View {
@State var data = ["Hit the Edit button to reorder", "Practice Coding", "Grocery shopping",
"Get tickets", "Clean house", "Do laundry", "Cook dinner", "Paint room"]

var body: some View {


NavigationView {
List {
ForEach(data, id: \.self) { datum in
Text(datum).font(Font.system(size: 24)).padding()
}
.onMove(perform: moveRow)
}
.navigationBarTitle(Text("To Do"))
.navigationBarItems(trailing: EditButton()) // Add button to Nav bar
}.accentColor(.green) // Changes color of buttons
}

func moveRow(from indexes: IndexSet, to destination: Int) {


if let first = indexes.first {
data.insert(data.remove(at: first), at: destination)
}
}
}

What is EditButton()?
This is a built-in function that returns a view (Button) that will automatically toggle edit mode on the List.
Its text says “Edit” and then when tapped you will see the move handles appear on the rows and the
button text says “Done”.

www.bigmountainstudio.com 114 Free Sample (Get the Full Version Here)


List

List Row Background


struct Todo: Identifiable {
let id = UUID()
var action = ""
var due = ""
var isIndented = false
}

struct List_ListRowBackground : View {


@State private var newToDo = ""

@State var data = [


Todo(action: "Practice Coding", due: "Today"),
Todo(action: "Grocery shopping", due: "Today"),
Todo(action: "Get tickets", due: "Tomorrow"),
Todo(action: "Clean house", due: "Next Week"),
Todo(action: "Do laundry", due: "Next Week"),
Todo(action: "Cook dinner", due: "Next Week"),
Todo(action: "Paint room", due: "Next Week")
]

var body: some View {


List {
Section(header:
VStack {
Text("To Do").font(.title)
HStack {
TextField("new todo", text: $newToDo)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
self.data.append(Todo(action: self.newToDo))
self.newToDo = ""

www.bigmountainstudio.com 115 Free Sample (Get the Full Version Here)


List

List Row Background Continued


}) {
Image(systemName: "plus.circle.fill").font(.title)
}
}
}
.padding(
EdgeInsets(top: 50, leading: 16, bottom: 16, trailing: 16))
) {
ForEach(data) { datum in
Text(datum.action)
.font(Font.system(size: 24))
.foregroundColor(self.getTextColor(due: datum.due))
// Turn row green if due today
.listRowBackground(datum.due == "Today" ? Color.green : Color.clear)
.padding()
}
Notice the .listRowBackground function is on the view inside the ForEach. You want to call this function
}
}.edgesIgnoringSafeArea(.vertical) on whatever view will be inside the row, not on the List itself.
}

// This logic was inline but the compiler said it was "too complex" 🤷
func getTextColor(due: String) -> Color {
due == "Today" ? Color.black : Color.primary
}
}

www.bigmountainstudio.com 116
List

List Row Inset


struct List_ListRowInsets : View {
@State private var newToDo = ""

@State var data = [


Todo(action: "Practice using List Row Insets", due: "Today"),
Todo(action: "Grocery shopping", due: "Today"),
Todo(action: "Vegetables", due: "Today", isIndented: true),
Todo(action: "Spices", due: "Today", isIndented: true),
Todo(action: "Cook dinner", due: "Next Week"),
Todo(action: "Paint room", due: "Next Week")
]

var body: some View {


GeometryReader { gr in
List {
Section(header:
VStack {
Text("To Do")
.font(.title)
.foregroundColor(.white)
HStack {
TextField("new todo", text: self.$newToDo)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
self.data.append(Todo(action: self.newToDo))
self.newToDo = ""
}) {
Image(systemName: "plus.circle.fill")
.font(.title)
.foregroundColor(.white)
}

www.bigmountainstudio.com 117 Free Sample (Get the Full Version Here)


List

List Row Inset Continued


}
}
.padding(EdgeInsets(top: 50, leading: 26, bottom: 16, trailing: 16))
.background(Color.green)
.frame(width: gr.size.width)
) {
ForEach(self.data) { datum in
Text(datum.action)
.font(.title)
.padding()
// Inset row based on data
.listRowInsets(EdgeInsets(top: 0,
leading: datum.isIndented ? 60 : 20,
bottom: 0, trailing: 0))
}
}
}.edgesIgnoringSafeArea(.vertical)
}
}
}

www.bigmountainstudio.com 118
List

Headers and Footers


struct List_HeadersAndFooters : View {
var data = ["Evans", "Lemuel James Guerrero", "Mark", "Durtschi", "Chase", "Rodrigo"]
var body: some View {
List {
Section(header: Header(), footer: Text("7 People on Staff")) {
// ForEach will handle the row creation within the section
ForEach(data, id: \.self) { datum in
NameRow(content: datum)
}
}
}.edgesIgnoringSafeArea(.top)
}
}
// The views used for the header and footer
struct Header: View {
var body: some View {
Image("mountains")
.overlay(Text("STAFF")
.font(Font.system(size: 120, design: Font.Design.serif))
.foregroundColor(.green))
}
}
struct NameRow: View {
var content: String
var body: some View {
HStack {
Image("PineTree").foregroundColor(Color.green)
Text(content)
Spacer()

In order to set a foregroundColor on an image (“Pinetree”), that


image has to be set to render as a template image.

www.bigmountainstudio.com 119 Free Sample (Get the Full Version Here)


NavigationView

The NavigationView is a little different in that it will fill the whole screen when used. You will never have to specify its size.
But there are some ways you can customize it which you will see in the following pages.

This is a push-out view.

www.bigmountainstudio.com 120
NavigationView

Introduction
NavigationView {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe")
.font(.largeTitle)
Text("NavigationView")
.font(.largeTitle)
Text("Introduction")
.foregroundColor(.gray)

Text("Having a NavigationView will show nothing unless you also include a


navigationBarTitle modifier.")
.frame(maxWidth: .infinity)
.padding()
.background(Color("Theme3ForegroundColor"))
.foregroundColor(Color("Theme3BackgroundColor"))
.layoutPriority(1)

Spacer()
}
.font(.title)
.padding(.top, 25)
}
// This creates a title in your nav bar
.navigationBarTitle(Text("Navigation Views"))
.edgesIgnoringSafeArea(.bottom)
}

The navigationBarTitle goes INSIDE the NavigationView, not on it. Notice the default style of the title is large.

www.bigmountainstudio.com 121 Free Sample (Get the Full Version Here)


NavigationView

Display Mode
NavigationView {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe")
.font(.largeTitle)
Text("NavigationView")
.font(.largeTitle)
Text("Display Mode")
.foregroundColor(.gray)

Text("When you create a navigation bar title, you can specify if you want it large
or small (inline) or just automatic.")
.frame(maxWidth: .infinity)
.padding()
.background(Color("Theme3ForegroundColor"))
.foregroundColor(Color("Theme3BackgroundColor"))
.layoutPriority(1)

Spacer()
}
.font(.title)
.padding(.top, 25)
}
// Use .inline for the smaller nav bar
.navigationBarTitle(Text("Navigation Views"), displayMode: .inline)
.edgesIgnoringSafeArea(.bottom)
}

www.bigmountainstudio.com 122 Free Sample (Get the Full Version Here)


NavigationView

NavigationBarHidden
struct Navigation_BarHidden: View {
@State private var isHidden = true

var body: some View {


NavigationView {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView").font(.largeTitle)
Text("Navigation Bar Hidden")
.foregroundColor(.gray)
Image("NavBarHidden")
Text("If you don't want to show a navigation bar, you can use the
navigationBarHidden modifier to hide it.")
...
Toggle("Hide Nav Bar", isOn: $isHidden)
.padding()

Spacer()
}
.font(.title)
.padding(.top, 70)
}
// For some reason, you have to have a bar title for the hidden modifier to work.
.navigationBarTitle(Text("Navigation Views"))
// Hide when the Toggle is on
.navigationBarHidden(isHidden)
.edgesIgnoringSafeArea(.bottom)
}
}
}

www.bigmountainstudio.com 123 Free Sample (Get the Full Version Here)


NavigationView

NavigationBarItems
NavigationView {
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView")
Text("Navigation Bar Items").foregroundColor(.gray)
Image("NavBarItems")
Text("You can add navigation bar buttons to the leading or trailing (or both) sides of a
navigation bar.")
...
Text("Use the accentColor modifier on the navigation bar items to change them from the
default accent color.")
...
Spacer()
}
.font(.title)
.padding(.top, 25)
.navigationBarTitle(Text("Navigation Bar Items"),
displayMode: .inline)
// Add some bar items to the nav bar
.navigationBarItems(
// Button on the leading side
leading:
Button(action: { }) {
Image(systemName: "bell.fill")
}.accentColor(.pink)
// Button on the trailing side
, trailing:
Button("Actions", action: { })
.accentColor(.pink)
)
}

www.bigmountainstudio.com 124 Free Sample (Get the Full Version Here)


NavigationView

NavigationBarBackButtonHidden

You can hide the back button in


the navigation bar for views by
using a modifier.

This is good in scenarios where you


supply another button to navigate
the user back or you want to supply
your own custom back button (see
next example for custom back
button).

www.bigmountainstudio.com 125 Free Sample (Get the Full Version Here)


NavigationView

NavigationBarBackButtonHidden - Code
// First Screen
struct Navigation_BackButtonHidden: View {
var body: some View {
NavigationView {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView").font(.largeTitle)
Text("Back Button Hidden").foregroundColor(.gray)
Image("NavBarBackButtonHidden")

NavigationLink("Go To Detail",
destination: BackButtonHiddenDetail())
Spacer()
}
.font(.title)
.padding(.top, 70)
}
.navigationBarTitle(Text("Navigation Views"))
.edgesIgnoringSafeArea(.bottom)
}
}
}
// Second Screen
struct BackButtonHiddenDetail: View {
@Environment(\.presentationMode) var presentationMode

var body: some View {


ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView").font(.largeTitle)
Text("Back Button Hidden").foregroundColor(.gray)
Image("NavBarBackButtonHidden")
Text("This nav bar has no back button because it was hidden on this view.")

www.bigmountainstudio.com 126 Free Sample (Get the Full Version Here)


NavigationView

NavigationBarBackButtonHidden - Code
.frame(maxWidth: .infinity)
.padding()
.background(Color("Theme3ForegroundColor"))
.foregroundColor(Color("Theme3BackgroundColor"))
.layoutPriority(1)

Button("Go Back") {
self.presentationMode.wrappedValue.dismiss()
}

Spacer()
}
.font(.title)
.padding(.top, 50)
}
.navigationBarTitle(Text("Detail View"), displayMode: .inline)
.edgesIgnoringSafeArea(.bottom)
// Hide the back button
.navigationBarBackButtonHidden(true)
}
}

www.bigmountainstudio.com 127 Free Sample (Get the Full Version Here)


NavigationView

Custom Back Button


struct Navigation_CustomBackButton_Detail: View {
@Environment(\.presentationMode) var presentationMode

var body: some View {


ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
...
Text("Hide the system back button and then use the navigation bar items modifier
to add your own.")
...
Spacer()
}
.font(.title)
.padding(.top, 50)
}
.navigationBarTitle(Text("Detail View"), displayMode: .inline)
.edgesIgnoringSafeArea(.bottom)
// Hide the system back button
.navigationBarBackButtonHidden(true)
// Add your custom back button here
.navigationBarItems(leading:
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.left.circle")
Text("Go Back")
}
})
}
}

www.bigmountainstudio.com 128 Free Sample (Get the Full Version Here)


Picker

To get or set a value for the Picker, you need to bind it to a variable. This variable is then passed into the Picker’s initializer.
Then, all you need to do is change this bound variable’s value to select the row you want to show in the Picker. Or read the
bound variable’s value to see which row is currently selected. One thing to note is that this variable is actually bound to the
Picker row’s tag property which you will see in the following pages.

www.bigmountainstudio.com 129
Picker

Introduction
struct Picker_Intro : View {
@State private var favoriteState = 1
@State private var yourName = "Mark"
var body: some View {
VStack(spacing: 20) {
Text("Picker").font(.largeTitle)
Text("Introduction").font(.title).foregroundColor(.gray)

Text("You associate a variable with the picker rows' tag values")


.frame(maxWidth: .infinity).padding().font(.title)
.background(Color("AccentColorDark"))
.foregroundColor(Color.white)
Picker(selection: $favoriteState, label: Text("States")) {
Text("California").tag(0)
Text("Utah").tag(1)
Text("Vermont").tag(2)
}.padding(.horizontal)

Text("Tag values can be String, Int or Bool.")


.frame(maxWidth: .infinity).padding().font(.title)
.background(Color("AccentColorDark"))
.foregroundColor(Color.white)
Picker(selection: $yourName, label: Text("Your name")) {
Text("Paul").tag("Paul")
Text("Chris").tag("Chris")
Text("Mark").tag("Mark")
Text("Scott").tag("Scott")
Text("Meng").tag("Meng")
}.padding(.horizontal)
}
}
}

www.bigmountainstudio.com 130 Free Sample (Get the Full Version Here)


Picker

Customized
struct Picker_Customized : View {
@State private var favoriteState = 1
@State private var youTuberName = "Mark"
var body: some View {
VStack(spacing: 30) {
Text("Picker").font(.largeTitle)
Text("With Modifiers").foregroundColor(.gray)
Text("Your Favorite State:")
Picker(selection: $favoriteState, label: Text("")) {
Text("California").tag(0)
Text("Utah").tag(1)
Text("Vermont").tag(2)
}
.foregroundColor(Color.white).padding(.horizontal)
.background(Color("AccentColorDark"))
.cornerRadius(15)
.shadow(radius: 5)
Text("Who do you want to watch today?")
Picker(selection: $youTuberName, label: Text("")) {
Text("Paul").tag("Paul")
Text("Chris").tag("Chris")
Text("Mark").tag("Mark")
Text("Scott").tag("Scott")
Text("Meng").tag("Meng")
}
.padding(.horizontal)
.background(RoundedRectangle(cornerRadius: 15)
.stroke(Color.blue, lineWidth: 1))
}
.labelsHidden() // Show no labels on pickers
.font(.title)
}
}

www.bigmountainstudio.com 131 Free Sample (Get the Full Version Here)


Picker

Rows with Images


VStack(spacing: 20) {
Text("Picker").font(.largeTitle)
Text("Rows with Images").font(.title).foregroundColor(.gray)
Text("Who do you want to watch today?")
.padding(.top)

Picker(selection: $youTuberName, label: Text("")) {


Row(name: "Sean")
Row(name: "Chris")
Row(name: "Mark")
Row(name: "Scott")
Row(name: "Paul")
}
.foregroundColor(Color.white)
.padding(.horizontal)
.background(Color("AccentColorDark"))
.cornerRadius(15)
.shadow(radius: 20)
.labelsHidden()
}

fileprivate struct Row : View {


var name: String
var body: some View {
return HStack {
Image(systemName: "person.fill")
.padding(.trailing)
.foregroundColor(Color.red)
Text(name)
}
.tag(name)
}
}

www.bigmountainstudio.com 132 Free Sample (Get the Full Version Here)


Picker

Binding Rows to Data


@State private var youTuberName = "Mark"
var youTubers = ["Sean", "Chris", "Mark", "Scott", "Paul"]
...
VStack(spacing: 20) {
Text("Picker").font(.largeTitle)
Text("Binding to Data").foregroundColor(.gray)
Text("Use a ForEach with your Picker view to populate it with data.")
.frame(maxWidth: .infinity).padding()
.background(Color("AccentColorDark"))
.foregroundColor(Color.white)

Text("Who do you want to watch today?")


.padding(.bottom, 0)

Picker(selection: $youTuberName, label: Text("")) {


ForEach(youTubers, id: \.self) { name in
Row(name: name)
}
}
.labelsHidden()
}.font(.title)

struct Row : View {


var name: String
var body: some View {
HStack {
Image(systemName: "person.fill")
Text(name)
}.tag(name)
}
}

www.bigmountainstudio.com 133 Free Sample (Get the Full Version Here)


ScrollView

A ScrollView is like a container for child views. When the child views within the ScrollView go outside the frame, the user can
scroll to bring the child views that are outside the frame into view.

A ScrollView is a push-out view in the scroll direction you specify. You can set the direction of a ScrollView to be vertical or
horizontal.

www.bigmountainstudio.com 134
ScrollView

Introduction
@State private var names = ["Scott", "Mark", "Chris", "Sean", "Rod", "Meng", "Natasha", "Chase",
"Evans", "Paul", "Durtschi", "Max"]
...
NavigationView {
GeometryReader { gr in
ScrollView {
ForEach(self.names, id: \.self) { name in
NavigationLink(destination: DetailView(name: name)) {
HStack {
Text(name).foregroundColor(.primary)
Image(systemName: "checkmark.seal.fill")
.foregroundColor(.green)
Spacer()
Image(systemName: "chevron.right.circle.fill")
}
.font(.system(size: 24, design: .rounded))
.padding().background(Color.white)
.cornerRadius(8)
.shadow(radius: 1, y: 1)
}
} // Set the width on the ForEach (it's a View)
.frame(width: gr.size.width - 32)
.accentColor(Color.pink)
.padding()
}
.navigationBarTitle(Text("Cool People"))
}
}

A Scrollview with a ForEach view is similar to a List. But be warned, the rows are not reusable. It is best
to limit the number of rows for memory and performance considerations.

www.bigmountainstudio.com 135 Free Sample (Get the Full Version Here)


ScrollView

Scroll Horizontally
struct Scrollview_Horizontal : View {
var items = [Color.green, Color.blue, Color.purple, Color.pink,
Color.yellow, Color.orange]

var body: some View {


GeometryReader { gr in
VStack(spacing: 20) {
Text("ScrollView")
.font(.largeTitle)
Text("Scroll Horizontally")
.font(.title)
.foregroundColor(.gray)
Text("Just set the ScrollView's axis to horizontal and if the contents go
horizontally beyond the frame, scrolling will be enabled.")
...
ScrollView(Axis.Set.horizontal, showsIndicators: true) {
HStack {
ForEach(self.items, id: \.self) { item in
RoundedRectangle(cornerRadius: 15)
.fill(item)
.frame(width: gr.size.width - 60,
height: 200)
}
}
}
.padding(.horizontal)
Spacer()
}
}
}
}

www.bigmountainstudio.com 136 Free Sample (Get the Full Version Here)


SecureField

In order to get or set the text in a SecureField, you need to bind it to a variable. This variable is passed into the SecureField’s
initializer. Then, all you need to do is change this bound variable’s text to change what is in the SecureField. Or read the
bound variable’s value to see what text is currently in the SecureField.

This is a pull-in control.

www.bigmountainstudio.com 137
SecureField

Introduction
@State private var userName = ""
@State private var password = ""
...
VStack(spacing: 20) {
Image("Logo")
.padding(.bottom, 150)

Text("SecureField")
.font(.largeTitle)

Text("Introduction")
.font(.title)
.foregroundColor(.gray)

Text("SecureFields, like TextFields, need to bind to a local variable.")


...

TextField("user name", text: $userName)


.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()

SecureField("password", text: $password)


.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()

Spacer()
}

www.bigmountainstudio.com 138 Free Sample (Get the Full Version Here)


SecureField

Customizations
@State private var userName = ""
@State private var password = ""

...

Text("Use a ZStack to put a RoundedRectangle behind a SecureField with a plain textfieldStyle.")


...

ZStack{
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.purple)
TextField("user name", text: $userName)
.foregroundColor(Color.white)
.padding(.horizontal)
}
.frame(height: 40)
.padding(.horizontal)

Text("Or overlay the SecureField on top of another view or shape.")


...

RoundedRectangle(cornerRadius: 8)
.foregroundColor(.purple)
.overlay(
SecureField("password", text: $password)
.foregroundColor(Color.white)
.padding(.horizontal)
)
.frame(height: 40)
.padding(.horizontal)

www.bigmountainstudio.com 139 Free Sample (Get the Full Version Here)


SecureField

Customization Layers
@State private var userName = ""
@State private var password = ""
...
VStack(spacing: 20) {
Text("SecureField")
.font(.largeTitle)
Text("Customization Layers")
.font(.title)
.foregroundColor(.gray)
Text("You can also add a background to the SecureField. It's all the same idea adjust the
layers.")
...

SecureField("password", text: $password)


.foregroundColor(Color.white)
.frame(height: 40)
.padding(.horizontal)
.background(
Capsule()
.foregroundColor(.purple)
)
.padding(.horizontal)

Image("SecureFieldLayers")

Text("The highlighted layer in that image is the actual text field layer of the view.")
.font(.title)
.padding(.horizontal)
}

www.bigmountainstudio.com 140 Free Sample (Get the Full Version Here)


Segmented Control (Picker)

Segmented controls are now Picker controls with a different picker style set. In order to get or set the selected segment, you
need to bind it to a variable. This variable is passed into the segmented control’s (Picker’s) initializer. Then, all you need to
do is change this bound variable’s value to change the selected segment. Or read the bound variable’s value to see which
segment is currently selected.

This is a pull-in view.

www.bigmountainstudio.com 141
Segmented Control (Picker)

Introduction
@State private var dayNight = "day"
@State private var tab = 1

...

VStack(spacing: 20) {
Text("Segmented Control (Picker)").font(.largeTitle)
Text("Introduction")
.font(.title).foregroundColor(.gray)
Text("Associate the segmented control with an @State variable that will control which
segment is selected. The state variable will match each segment's tag value.")
...

Picker("", selection: $dayNight) {


Text("Day").tag("day")
Text("Night").tag("night")
}
.pickerStyle(SegmentedPickerStyle())
.padding()

Text("With Images:")

Picker("", selection: $tab) {


Image(systemName: "sun.min").tag(0)
Image(systemName: "moon").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
}

www.bigmountainstudio.com 142 Free Sample (Get the Full Version Here)


Segmented Control (Picker)

No Segment Selected
@State private var selection = 0

...

VStack(spacing: 20) {
Text("Segmented Control (Picker)").font(.largeTitle)
Text("No Segment Selected")
.font(.title).foregroundColor(.gray)
Text("This segmented control will have nothing selected because the default state variable
does not match any of the segment tag values.")
...

Text("How many meals do you eat?")


.foregroundColor(.gray)
.font(.title)

Picker("", selection: $selection) {


Text("One").tag(1)
Text("Two").tag(2)
Text("Three").tag(3)
Text("More").tag(4)
}
.pickerStyle(SegmentedPickerStyle())
.background(RoundedRectangle(cornerRadius: 8)
.stroke(Color.red, lineWidth: selection == 0 ? 1 : 0))
.padding()

Text("The red outline will go away once a selection is made.")


...
}

www.bigmountainstudio.com 143 Free Sample (Get the Full Version Here)


Segmented Control (Picker)

Colors
@State private var selection = 2
...
VStack(spacing: 20) {
Text("Segmented Control (Picker)").font(.largeTitle)
Text("Colors")
.font(.title).foregroundColor(.gray)
Text("You can change the color of segmented controls by using the background modifier.")
...

Text("When you add a color, notice the corners:")


Picker("", selection: $selection) {
Text("One").tag(1)
Text("Two").tag(2)
Text("Three").tag(3)
}
.pickerStyle(SegmentedPickerStyle())
.background(Color.yellow)
.padding(.horizontal)

Text("Adding a corner radius should handle it:")


Picker("", selection: $selection) {
Text("One").tag(1)
Text("Two").tag(2)
Text("Three").tag(3)
}
.pickerStyle(SegmentedPickerStyle())
.background(Color.yellow)
.cornerRadius(8)
.padding(.horizontal)
}

www.bigmountainstudio.com 144 Free Sample (Get the Full Version Here)


Slider
When using a Slider view, the default range of values is 0.0 to 1.0. You bind the Slider to a state variable, usually a number
type, like an Int. But it doesn’t have to be a number type. It can be any type that conforms to the Stridable protocol. (“Stride”
means to “take steps in a direction; usually long steps”.) A type that conforms to Stridable (such as an Int) means it has
values that are continuous and can be stepped through and measured. (“Step through”, “Stride”, I think you see the
connection now.)

You use the bound variable to set or get the value the Slider’s thumb (circle) is currently at.

This is a pull-in view.

www.bigmountainstudio.com 145
Slider

Introduction
@State private var sliderValue = 0.5
@State private var age = 18.0

let ageFormatter: NumberFormatter = {


let numFormatter = NumberFormatter()
numFormatter.numberStyle = .spellOut
return numFormatter
}()
...
Text("Associate the Slider with an @State variable that will control where the thumb (circle)
will be on the slider's track.")
...
Text("Default min value is 0.0 and max value is 1.0")
...
Group {
Text("Value is: ") +
Text("\(sliderValue)").foregroundColor(.green)
}
.font(.title)

Text("You can also set your own min and max value.")
....

Text("What is your age?").font(.title)


Slider(value: $age, in: 1...100, step: 1)
.padding(.horizontal)

Group {
Text("Age is: ") +
Text("\(ageFormatter.string(from: NSNumber(value: age))!)")
.foregroundColor(.pink)
}
.font(.title)

www.bigmountainstudio.com 146 Free Sample (Get the Full Version Here)


Slider

Customization
@State private var sliderValue = 0.5
...
Text("At the time of this writing, there isn't a way to color the thumb. But we can change the
background color and apply some other modifiers.")
...
Slider(value: $sliderValue)
.padding(.horizontal, 10)
.background(Color.orange)
.cornerRadius(.infinity) // Rounded ends
.shadow(color: .gray, radius: 2)
.padding(.horizontal)

Text("Use the accentColor modifier to change the color of the track.")


...

Slider(value: $sliderValue)
.padding(.horizontal)
.accentColor(.orange)

Text("Using shapes and outlines.")


...

Slider(value: $sliderValue)
.padding(10)
.background(Capsule().stroke(Color.orange, lineWidth: 2))
.padding(.horizontal)

Slider(value: $sliderValue)
.padding(10)
.background(Capsule().fill(Color.orange))
.accentColor(.black)
.padding(.horizontal)

www.bigmountainstudio.com 147 Free Sample (Get the Full Version Here)


Slider

With Images
Text("Combine the slider with images in an HStack, VStack or both!")
...

HStack {
Image(systemName: "tortoise")
Slider(value: $sliderValue)
Image(systemName: "hare")
}.foregroundColor(.green).padding()

HStack {
Image(systemName: "speaker.fill")
Slider(value: $sliderValue)
Image(systemName: "speaker.3.fill")
}
.foregroundColor(.accentColor)
.padding()

VStack {
Slider(value: $sliderValue)
.accentColor(.orange)
HStack {
Image(systemName: "circle")
Spacer()
Image(systemName: "circle.righthalf.fill")
Spacer()
Image(systemName: "circle.fill")
}
.foregroundColor(.orange)
.padding(.top, 8)
}.padding()

www.bigmountainstudio.com 148 Free Sample (Get the Full Version Here)


Stepper

When using a Stepper view, you bind it to a state variable, usually a number. But it doesn’t have to be a number type. It can
be any type that conforms to the Stridable protocol. (“Stride” means to “take steps in a direction; usually long steps”.) A type
that conforms to Stridable means it has values that are continuous and can be stepped through and measured. (“Step
through”, “Stride”, I think you see the connection now.)
You use the bound variable to set or get the value it is currently at.
This is a horizontal push-out view. Vertically it is pull-in.

www.bigmountainstudio.com 149
Stepper

Introduction
@State private var stepperValue = 1
@State private var values = [0, 1]
...
VStack(spacing: 20) {
Text("Stepper")
.font(.largeTitle)
Text("Introduction")
.font(.title).foregroundColor(.gray)
Text("The Stepper can be bound to a variable like this:")
...

Stepper(value: $stepperValue) {
Text("Bound Stepper: \(stepperValue)")
}.padding(.horizontal)
Divider()
Image(systemName: "bolt.fill")
.font(.title).foregroundColor(.yellow)
Text("Or you can run code on the increment and decrement events:")
.frame(maxWidth: .infinity).padding()
.background(Color.blue).foregroundColor(Color.white)
.font(.title)
Stepper(onIncrement: {self.values.append(self.values.count)},
onDecrement: {self.values.removeLast()}) {
Text("onIncrement and onDecrement")
}.padding(.horizontal)
HStack {
ForEach(values, id: \.self) { value in
Image(systemName: "\(value).circle.fill")
}
}.font(.title).foregroundColor(.green)
}

www.bigmountainstudio.com 150 Free Sample (Get the Full Version Here)


Stepper

Label Options
struct Stepper_LabelsHidden: View {
@State private var stepperValue = 1

var body: some View {


VStack(spacing: 20) {
Text("Stepper").font(.largeTitle)
Text("Label Options").foregroundColor(.gray)
Text("You can declare a stepper with just a string value for the label.")
.frame(maxWidth: .infinity).padding()
.background(Color.blue).foregroundColor(Color.white)
Stepper("What is your age?", value: $stepperValue)
.padding(.horizontal)
Text("You can omit a label too. Notice how the stepper view still pushes out
horizontally.")
.frame(maxWidth: .infinity).padding()
.background(Color.blue).foregroundColor(Color.white)
Stepper("", value: $stepperValue)
.padding(.horizontal)
Text("If you hide the label then no space will be reserved for it.")
.frame(maxWidth: .infinity).padding()
.background(Color.blue).foregroundColor(Color.white)
Stepper("What is your age?", value: $stepperValue)
.padding(.horizontal)
.labelsHidden()
}.font(.title)
}
}

Note: Even though the label/title is not shown, I would still recommend having one because it will still
be used for accessibility purposes.

www.bigmountainstudio.com 151 Free Sample (Get the Full Version Here)


Stepper

Range
@State private var stars = 0

VStack(spacing: 20) {
Text("Stepper")
.font(.largeTitle)
.padding()
Text("Range of Values")
.font(.title)
.foregroundColor(.gray)
Text("You can set a range for the stepper too. In this example, the range is between one and
five.")
...

Stepper(value: $stars, in: 1...5) {


Text("Rating")
}.padding(.horizontal)

HStack {
ForEach(1...stars, id: \.self) { star in
Image(systemName: "star.fill")
}
}
.font(.title)
.foregroundColor(.yellow)
}

When the Stepper reaches the range limits, the corresponding plus or minus button will appear as
disabled. In this screenshot, notice the plus button is disabled.

www.bigmountainstudio.com 152 Free Sample (Get the Full Version Here)


Stepper

Customization
@State private var contrast = 50
...
Text("A foreground and background color can be set.")
...

Stepper(onIncrement: {}, onDecrement: {}) {


Text("Custom Stepper")
.font(.title)
.padding(.vertical)
}
.padding(.horizontal)
.background(Color.blue)
.foregroundColor(.white)

Text("Notice the minus and plus buttons are not affected. The platforms determine how this will
be shown.")
...

Text("You can add images too.")


.frame(maxWidth: .infinity).padding()
.background(Color.blue).foregroundColor(Color.white)
.font(.title).layoutPriority(1)

Stepper(value: $contrast, in: 0...100) {


// SwiftUI implicitly uses an HStack here
Image(systemName: "circle.lefthalf.fill")
Text(" \(contrast)/100")
}
.font(.title)
.padding(.horizontal)
.foregroundColor(.blue)

www.bigmountainstudio.com 153 Free Sample (Get the Full Version Here)


Stepper

Colors
Text("There is no built-in way to change the color of the stepper that I have found. Instead, I
had to remove the text, resize its frame and apply a color behind it.")
...

Stepper(value: $contrast, in: 0...100) {


Text("Applying Accent Color (no effect)")
}
.accentColor(.blue)

HStack {
Text("My Custom Colored Stepper")
Spacer()
Stepper("", value: $contrast)
.frame(width: 95, height: 33)
.offset(x: -4)
.background(Color(UIColor.systemTeal))
.cornerRadius(9)
}

HStack {
Text("My Custom Colored Stepper")
Spacer()
Stepper("", value: $contrast)
.frame(width: 95, height: 33)
.offset(x: -4)
.background(Color.orange)
.cornerRadius(9)
}

www.bigmountainstudio.com 154 Free Sample (Get the Full Version Here)


TabView

The TabView acts like a container for child views within it. These child views are individual screens. It provides tab buttons
(TabItems) that allows the user to switch between these child views.

This is a push-out view.

www.bigmountainstudio.com 155
TabView

Introduction
TabView {
// First Screen
VStack(spacing: 20) {
Text("TabView")
.font(.largeTitle)
Text("Introduction")
.font(.title)
.foregroundColor(.gray)
Text("The TabView view can hold multiple views, one for each tab.")
...

Text("At the end of a view, you add .tabItem modifier to show a button that allows
navigation to that view.")
...

Image("TabItem")

}.tabItem {
// Creates a tab button in the tab bar
Text("Tab 1")
}

// Second Screen
Text("This view represents the Second Screen.")
.tabItem {
// Creates a tab button in the tab bar
Text("Tab 2")
}
}

www.bigmountainstudio.com 156 Free Sample (Get the Full Version Here)


TabView

TabItems
struct TabView_Tabs : View {
var body: some View {
TabView {
TabOne().tabItem {
Text("Tab Text")
}
Text("Phone Calls").tabItem {
Image(systemName: "phone")
}
Text("Outgoing Phone Calls").tabItem {
Image(systemName: "phone.arrow.up.right")
Text("Outgoing")
}
}
}
}

// A View within the TabView


struct TabOne: View {
var body: some View {
VStack(spacing: 20) {
Text("TabView")
.font(.largeTitle)
Text("TabItems")
.font(.title).foregroundColor(.gray)
Text("TabItems can accept Text, Image or both. Notice the order of Text and Image
does not matter for the tabItem.")
...
}
}
}

www.bigmountainstudio.com 157 Free Sample (Get the Full Version Here)


TabView

Too Many Tabs


TabView {
Text("Call Screen").tabItem {
Image(systemName: "phone")
Text("Call")
}
Text("Outgoing Phone Calls Screen").tabItem {
Image(systemName: "phone.arrow.up.right")
Text("Outgoing")
}
Text("Incoming Phone Calls Screen").tabItem {
Image(systemName: "phone.arrow.down.left")
Text("Incoming")
}
Text("Phone Book Screen").tabItem {
Image(systemName: "book")
Text("Phone Book")
}
Text("History Screen").tabItem {
Image(systemName: "clock")
Text("History")
}
Text("New Phone Number").tabItem {
Image(systemName: "phone.badge.plus")
Text("New")
}
}

When there are too many tabs to fit for the device, the More button is created where you can find the
rest of the tabs listed out.

www.bigmountainstudio.com 158 Free Sample (Get the Full Version Here)


TabView

Navigation
@State private var selectedTab = 1 // Set which tab is active
...
// Tell the TabView which variable to listen to for changes
TabView(selection: $selectedTab) {
// Tab 1
VStack(spacing: 20) {
Text("TabView").font(.largeTitle)
Text("Navigation")
.font(.title).foregroundColor(.gray)
Text("Add a unique tag value to each screen (view) you want to programmatically navigate
to. You can then bind a variable to the TabView's selection property and change that variable to
navigate.")
...

Button("Go to Tab 3") {


self.selectedTab = 3
}.font(.title)

}.tabItem {
Image(systemName: "star.fill")
}.tag(1)

// Tab 2
Text("Second Screen")
.tabItem {
Image(systemName: "moon.fill")
}.tag(2)

// Tab 3
Text("Third Screen")
.tabItem {
Image(systemName: "sun.min.fill")
}.tag(3)
}

www.bigmountainstudio.com 159 Free Sample (Get the Full Version Here)


TabView

Colors
TabView {
// Tab 1
ZStack {
Color.blue.colorMultiply(.red).edgesIgnoringSafeArea(.top)
VStack(spacing: 20) {
Text("TabView")
.font(.largeTitle).foregroundColor(.white)
Text("TabItem Colors")
.font(.title).foregroundColor(.gray)
Text("Set the color of the active tab item by setting the accent color for the
TabView.")
...
}
}.tabItem {
Image(systemName: "star.fill")
}
// Tab 2
Text("Second Screen").tabItem {
Image(systemName: "moon.fill")
}.foregroundColor(Color.red)
// Tab 3
Text("Third Screen").tabItem {
Image(systemName: "sun.min.fill")
}
}
.edgesIgnoringSafeArea(.top)
.accentColor(.yellow)

Notice that I am setting the foreground color of the second tabItem to red. This will have no effect on
the color of the tab item. The background modifier will not work either.

www.bigmountainstudio.com 160 Free Sample (Get the Full Version Here)


Text

The text view will probably be one of your most-used views. It has many, if not the most, modifiers available to it.

This is a pull-in view.

www.bigmountainstudio.com 161
Text

Line Limit
VStack(spacing: 20) {
Text("Text")
.font(.largeTitle)

Text("Wrapping")
.font(.title)
.foregroundColor(.gray)

Image("LineLimit")

Text("The Text view shows read-only text that can be modified in many ways. It wraps
automatically. If you want to limit the text wrapping, add .lineLimit(<number of lines here>).")
...

Text("Here, I am limiting the text to just one line.")


.lineLimit(1)
.font(.title)
.padding(.horizontal)
}

www.bigmountainstudio.com 162 Free Sample (Get the Full Version Here)


Text

Text Styles
VStack(spacing: 20) {
Text("Text")
.font(.largeTitle)

Text("Text Styles")
.font(.title)
.foregroundColor(.gray)

Image("Font")

Text("You can add a TextStyle to the Text view by calling .font(Font.<Text Style>).")
...

Text("Available Text Styles")


.font(.title)
.foregroundColor(.gray)

Group {
Divider()
Text("Font.largeTitle").font(.largeTitle)
Text("Font.title").font(.title)
Text("Font.headline").font(.headline)
Text("Font.subheadline").font(.subheadline)
Text("Font.body").font(.body)
Text("Font.callout").font(.callout)
Text("Font.caption").font(.caption)
Text("Font.footnote").font(.footnote)
}
}

www.bigmountainstudio.com 163 Free Sample (Get the Full Version Here)


Text

Weights
Text("Text")
.font(.largeTitle)
Text("Font Weights")
.font(.title)
.foregroundColor(.gray)
Image("FontWeight")
Text("You can apply a variety of font weights to the Text view.")
.padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.foregroundColor(.white)
.font(.title)
.layoutPriority(2)
Group { // Too many views (> 10) in one container
Text("Ultralight")
.fontWeight(.ultraLight)
Text("Thin")
.fontWeight(.thin)
Text("Light")
.fontWeight(.light)
Text("Regular")
.fontWeight(.regular)
Text("Medium")
.fontWeight(.medium)
Text("Semibold")
.fontWeight(.semibold)
Text("Bold")
.fontWeight(.bold)
Text("Heavy")
.fontWeight(.heavy)
Text("Black")
.fontWeight(.black)
}.font(.title)

www.bigmountainstudio.com 164 Free Sample (Get the Full Version Here)


Text

Weight & Text Style Combined


return VStack(spacing: 40) {
Text("Text")
.font(.largeTitle)
Text("Weight & Text Styles")
.font(.title)
.foregroundColor(.gray)
HStack {
Image("FontWeight")
Image(systemName: "plus")
Image("Font")
}

Text("These weights can be combined with Text Styles.")


...

Text("Ultralight - Title")
.fontWeight(.ultraLight)
.font(.title)
Text("Thin - Body")
.fontWeight(.thin)
.font(.body)
Text("Light - Large Title")
.fontWeight(.light)
.font(.largeTitle)
Text("Bold - Callout")
.fontWeight(.bold)
.font(.callout)
Text("Black - Title")
.fontWeight(.black)
.font(.title)
}
.edgesIgnoringSafeArea(.bottom)

www.bigmountainstudio.com 165 Free Sample (Get the Full Version Here)


Text

Font Design
VStack(spacing: 20) {
Text("Text")
.font(.largeTitle)

Text("Font Design")
.font(.title)
.foregroundColor(.gray)

Text("There are 4 font designs now in iOS. Use Font.system to set the font design you
want.")
...

Text("Default font design")


.font(Font.system(size: 36, design: Font.Design.default))

// You can remove the "Font.Design" of the enum


Text("Here is monospaced")
.font(Font.system(size: 36, design: .monospaced))

Text("And there is rounded")


.font(Font.system(size: 36, design: .rounded))

Text("Finally, we have serif!")


.font(Font.system(size: 36, design: .serif))
}

www.bigmountainstudio.com 166 Free Sample (Get the Full Version Here)


Text

Formatting
@State private var modifierActive = true
...
HStack {
Image("Bold")
Text("Bold").bold()
}
HStack {
Image("Italic")
Text("Italic").italic()
}
HStack {
Image("Strikethrough")
Text("Strikethrough").strikethrough()
}
HStack {
Image("Strikethrough")
Text("Green Strikethrough")
.strikethrough(modifierActive, color: .green)
}
HStack {
Image("ForegroundColor")
Text("Text Color (ForegroundColor)").foregroundColor(.green)
}
HStack {
Image("Underline")
Text("Underline").underline()
}
HStack {
Image("Underline")
Text("Green Underline").underline(modifierActive, color: .green)
}
...
Toggle("Modifiers Active", isOn: $modifierActive)

www.bigmountainstudio.com 167 Free Sample (Get the Full Version Here)


Text

Allows Tightening
VStack(spacing: 20) {
...

Image("AllowsTightening")

Text("You might want to tighten up some text that might be too long.")
...

Text("In the example below, the green has .allowTightening(true)")


...

Group {
Text("Allows tightening to allow text to fit in one line.")
.foregroundColor(.red)
.allowsTightening(false)
.padding(.horizontal)
.lineLimit(1)
Text("Allows tightening to allow text to fit in one line.")
.foregroundColor(.green)
.allowsTightening(true)
.padding(.horizontal)
.lineLimit(1)
}.padding(.horizontal)
}

Allows Tightening can be helpful when you see the last word getting truncated. Applying it may not even
fully work depending on just how much space can be tightened. With the default font, I notice I can get a
couple of characters worth of space to tighten up.

www.bigmountainstudio.com 168 Free Sample (Get the Full Version Here)


Text

Minimum Scale Factor


Text(".allowsTightening(true) is being used here:")
...

Text("Sometimes you want to shrink text if long")


.allowsTightening(true)
.font(.title)
.lineLimit(1)

Text("Allows tightening is failing here because the text is too long. In this case, you can
apply a minimum scale factor and specify by how much you want to allow the text to shrink.")
...

Image("MinimumScaleFactor")
Text(".minimumScaleFactor(0.5) is being used here:")
...

Text("Sometimes you want to shrink text if long")


.font(.title)
.lineLimit(1)
.minimumScaleFactor(0.5)

Text(".minimumScaleFactor takes a fraction from 0 to 1. For example, 0.3 is 30% of the original
size of the font that it can shrink. If the font size is 100, then it can shrink to 30.")

www.bigmountainstudio.com 169 Free Sample (Get the Full Version Here)


Text

Line Spacing
VStack(spacing: 10) {
Text("Text").font(.largeTitle)
Text("Line Spacing").font(.title).foregroundColor(.gray)
Image("LineSpacing")

Text("You can use line spacing to add more space between lines of text. This text has no
line spacing applied:")
.font(.title)
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
.foregroundColor(Color.white)
.layoutPriority(1)

Text("SwiftUI offers a Line Spacing modifier for situations where you want to increase the
space between the lines of text on the screen.")
.font(.title)

Text("With Line Spacing of 16:")


.font(.title)
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
.foregroundColor(Color.white)

Text("SwiftUI offers a Line Spacing modifier for situations where you want to increase the
space between the lines of text on the screen.")
.lineSpacing(16.0) // Add spacing between lines
.font(.title)
}

www.bigmountainstudio.com 170 Free Sample (Get the Full Version Here)


Text

Alignment
VStack(spacing: 20) {
Text("Text").font(.largeTitle)
Text("Multiline Text Alignment").foregroundColor(.gray)
Image("MultilineTextAlignment")
Text("By default, text will be centered within the screen. But when it wraps to multiple
lines, it will be leading aligned by default. Use multilineTextAlignment modifier to change
this!")
...

Text(".multilineTextAlignment(.center)")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.white)
.background(Color.green)

Text("Have I told you how awesome I think you are?")


.multilineTextAlignment(.center) // Center align
.padding(.horizontal)

Text(".multilineTextAlignment(.trailing)")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.white)
.background(Color.green)
.allowsTightening(true) // Prevent truncation

Text("You are SUPER awesome for improving your skills by using this book!")
.multilineTextAlignment(.trailing) // Trailing align
.padding(.horizontal)
}
.font(.title) // Apply this text style to all text views

www.bigmountainstudio.com 171 Free Sample (Get the Full Version Here)


Text

Truncation Mode
VStack(spacing: 20) {
Text("Text").font(.largeTitle)
Text("Truncation Mode").font(.title).foregroundColor(.gray)
Image("TruncationMode")
Text("When text gets truncated, you can control where the elipsis (...) shows.")
.frame(maxWidth: .infinity).padding()
.foregroundColor(.white).background(Color.green)
.font(.title).layoutPriority(1)
Text("Default: .truncationMode(.tail)")
.frame(maxWidth: .infinity).padding()
.foregroundColor(.white).background(Color.green)
.font(.title)
// Text automatically defaults at end
Text("This will be the best day of your life!")
.padding(.horizontal)
.lineLimit(1)
Text(".truncationMode(.middle)")
.frame(maxWidth: .infinity).padding()
.foregroundColor(.white).background(Color.green)
Text("This will be the best day of your life!")
.truncationMode(.middle) // Truncate in middle
.padding(.horizontal)
.lineLimit(1)
Text(".truncationMode(.head)")
.frame(maxWidth: .infinity).padding()
.foregroundColor(.white).background(Color.green)
Text("This will be the best day of your life!")
.truncationMode(.head) // Truncate at beginning
.padding(.horizontal)
.lineLimit(1)
}
.font(.title)

www.bigmountainstudio.com 172 Free Sample (Get the Full Version Here)


Text

Combining Modified Text


Group {
Text("You can ")
+ Text("format").bold()
+ Text (" different parts of your text by using the plus (+) symbol.")
}
...
Group {
Text("Here is another ")
+ Text("example").foregroundColor(.red).underline()
+ Text (" of how you might accomplish this. ")
+ Text("Notice").foregroundColor(.purple).bold()
+ Text (" the use of the Group view to add padding and line limit to all the text ")
+ Text("as a whole.").bold().italic()
}
.font(.title)
.padding(.horizontal)
.layoutPriority(1)

Group {
Text("You can also ").font(.title).fontWeight(.light)
+ Text("combine")
+ Text(" different font weights ").fontWeight(.black)
+ Text("and different text styles!").font(.title).fontWeight(.ultraLight)
}
.padding(.horizontal)

Although you see I’m wrapping my Text views in a Group, it is not required. I only do this so I can apply
common modifiers to everything within the Group. See section on the Group view for more information.

www.bigmountainstudio.com 173 Free Sample (Get the Full Version Here)


Text

Baseline Offset
VStack(spacing: 20) {
Text("Text")
.font(.largeTitle)
Text("Baseline Offset")
.font(.title).foregroundColor(.gray)

Image("BaselineOffset")

Text("By default, your combined text will be on the same baseline, like this:")
...

Text("100").underline()
+ Text(" SWIFTUI ").font(.largeTitle).fontWeight(.light)
.foregroundColor(.blue).underline()
+ Text ("VIEWS").underline()

Text("But you can offset each text view to create a cooler effect, like this:")
...

Group {
Text("100").bold()
+ Text(" SWIFTUI ")
.font(Font.system(size: 60))
.fontWeight(.ultraLight)
.foregroundColor(.blue)
.baselineOffset(-12) // Negative numbers go down
+ Text ("VIEWS").bold()
}
.layoutPriority(2)
}

www.bigmountainstudio.com 174 Free Sample (Get the Full Version Here)


Text

Layout Priority
Text("Text")
.font(.largeTitle)
Text("Layout Priority")
.font(.title)
.foregroundColor(.gray)

Image("LayoutPriority")

Text("Layout priority controls which view will get truncated last. The higher the priority, the
last it is in line to get truncated.")
.font(.title)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
.layoutPriority(2) // Highest priority to get the space it needs

Text("This text gets truncated first because it has no priority.")


.font(.title)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(Color.pink)

Text("The text view above got truncated because its layout priority is zero (the default). This
text view has a priority of 1. The text view on top has a priority of 2.")
.font(.title)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
.layoutPriority(1) // Next highest priority

www.bigmountainstudio.com 175 Free Sample (Get the Full Version Here)


Text

Custom Fonts
Text("Use a font that already exists on the system. If the font name doesn't exist, it goes back
to the default font.")
...
Text("This font doesn't exist")
.font(Font.custom("No Such Font", size: 26))

Text("Existing fonts:")
...

Text("Avenir Next")
.font(Font.custom("Avenir Next", size: 26))

Text("Gill Sans")
.font(Font.custom("Gill Sans", size: 26))

Text("Helvetica Neue")
.font(Font.custom("Helvetica Neue", size: 26))

Text("Adust the weight:")


...

Text("Avenir Next Regular")


.font(Font.custom("Avenir Next Regular", size: 26))

Text("Or change with the weight modifier:")


.foregroundColor(.red)

Text("Avenir Next Regular Weight")


.font(Font.custom("Avenir Next", size: 26).weight(.regular))

www.bigmountainstudio.com 176 Free Sample (Get the Full Version Here)


Text

Imported Fonts
struct Text_CustomFont: View {
var body: some View {
VStack(spacing: 20) {
Text("Text")
.font(.largeTitle)

Text("Imported Fonts")
.font(.title)
.foregroundColor(.gray)

Text("Use the Font.custom() function to set imported fonts you added to your
project.")
...

Text("Hello, World!")
.font(Font.custom("Nightcall", size: 60))
.padding(.top)
}
}
}

In order for this to work, you have to add the font file to your project and be sure to have the font file
target your project. Then you need to add the font file name to the Info.plist under the “Fonts provided
by application” key:

www.bigmountainstudio.com 177 Free Sample


TextField

In order to get or set the text in a TextField, you need to bind it to a variable. This variable is passed into the TextField’s
initializer. Then, all you need to do is change this bound variable’s text to change what is in the TextField. Or read the bound
variable’s value to see what text is currently in the TextField.

This is a push-out horizontally view.

www.bigmountainstudio.com 178
TextField

Introduction
VStack(spacing: 20) {
Group {
Text("TextField").font(.largeTitle)
Text("Introduction").font(.title).foregroundColor(.gray)
Text("It is required to bind text fields to a variable when using them so you can get/
set the text.")
...
}
Text("TextFieldStyle")
.font(.title)
.foregroundColor(.gray)

Text("By default, TextFields have a plain TextFieldStyle that has no visual content to be
seen.")
...
Image(systemName: "arrow.down.circle")
.font(.title)

TextField("", text: $textFieldData)

Image(systemName: "arrow.up.circle")
.font(.title)

Text("Use .textFieldStyle(RoundedBorderTextFieldStyle()) to show a border.")


...
}

www.bigmountainstudio.com 179 Free Sample (Get the Full Version Here)


TextField

Title (Placeholder or Hint Text)


@State private var textFieldData = ""
@State private var username = ""
@State private var password = ""
...
VStack(spacing: 20) {
Text("TextField")
.font(.largeTitle)

Text("Title Text (Placeholder or Hint)")


.font(.title)
.foregroundColor(.gray)

Text("You can supply title text (placeholder/hint text) through the first parameter to let
the user know the purpose of the text field.")
...

Group {
TextField("Here is title text", text: $textFieldData)
.textFieldStyle(RoundedBorderTextFieldStyle())

TextField("User name", text: $username)


.textFieldStyle(RoundedBorderTextFieldStyle())

TextField("Password", text: $password)


.textFieldStyle(RoundedBorderTextFieldStyle())

}
.padding(.horizontal)
}

www.bigmountainstudio.com 180 Free Sample (Get the Full Version Here)


TextField

Text Alignment
struct TextField_Alignment: View {
@State private var textFieldData1 = "Leading"
@State private var textFieldData2 = "Center"
@State private var textFieldData3 = "Trailing"

var body: some View {


VStack(spacing: 20) {
Text("TextField").font(.largeTitle)
Text("Text Alignment").font(.title).foregroundColor(.gray)
Text("Change the alignment of text within your textfield by using the
multilineTextAlignment modifier.")
.frame(maxWidth: .infinity).padding()
.background(Color.orange)
.font(.title)

Group {
TextField("Leading", text: $textFieldData1)
.textFieldStyle(RoundedBorderTextFieldStyle())
.multilineTextAlignment(.leading) // Default

TextField("Center", text: $textFieldData2)


.textFieldStyle(RoundedBorderTextFieldStyle())
.multilineTextAlignment(.center)

TextField("Trailing", text: $textFieldData3)


.textFieldStyle(RoundedBorderTextFieldStyle())
.multilineTextAlignment(.trailing)
}
.padding(.horizontal)
}
}
}

www.bigmountainstudio.com 181 Free Sample (Get the Full Version Here)


TextField

Text Size and Fonts


VStack(spacing: 20) {
Text("TextField")
.font(.largeTitle)
Text("With Text Modifiers")
.font(.title)
.foregroundColor(.gray)

Image("Font")

Text("To change the size of the font used within the TextField, you just need to use the
font modifier.")
...

Group {
TextField("first name", text: $textFieldData)
.font(.title)
.textFieldStyle(RoundedBorderTextFieldStyle())

TextField("first name", text: $textFieldData)


.font(Font.system(size: 36, design: .monospaced))
.textFieldStyle(RoundedBorderTextFieldStyle())

TextField("first name", text: $textFieldData)


.font(Font.system(size: 20, design: Font.Design.serif))
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(.horizontal)

Text("Notice this also changes the placeholder or hint text in the text field.")
...
}

www.bigmountainstudio.com 182 Free Sample (Get the Full Version Here)


TextField

Customizing Colors
VStack(spacing: 20) {
Text("TextField")
.font(.largeTitle)
Text("Customizing")
.font(.title)
.foregroundColor(.gray)
Text("One way to customize TextFields is to add a shape behind one that has no
TextFieldStyle set.")
...
TextField("Placeholder Text", text: $textFieldNoText)
.padding(10)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color(hue: 0.126, saturation: 0.47, brightness: 0.993)))
.padding()

TextField("Placeholder Text", text: $withOutline)


.padding(10)
.overlay(
// Add the outline
RoundedRectangle(cornerRadius: 8)
.stroke(Color.orange, lineWidth: 2)
)
.padding()

Text("Change text color using the foregroundColor property.")


...

TextField("first name", text: $textFieldWithText)


.textFieldStyle(RoundedBorderTextFieldStyle())
.foregroundColor(.orange)
.padding(.horizontal)
}

www.bigmountainstudio.com 183 Free Sample (Get the Full Version Here)


TextField

Custom Placeholder/Hint Text


@State private var textFieldData = ""
...
Text("There currently is not a way to customize the placeholder text. You can create your own
placeholder text behind the text field.")
...
Group {
ZStack(alignment: .leading) {
// Only show custom hint text if there is no text entered
if textFieldData.isEmpty {
Text("Enter name here").bold()
.foregroundColor(Color(.systemGray4))
}
TextField("", text: $textFieldData)
}
.padding(EdgeInsets(top: 4, leading: 10, bottom: 4, trailing: 10))
.overlay(
// Add the outline
RoundedRectangle(cornerRadius: 8)
.stroke(Color.orange, lineWidth: 2))

ZStack(alignment: .leading) {
if textFieldData.isEmpty {
Text("Email Address").italic()
.foregroundColor(.orange)
.opacity(0.4)
}
TextField("", text: $textFieldData)
}
.padding(EdgeInsets(top: 4, leading: 10, bottom: 4, trailing: 10))
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.orange, lineWidth: 2))
}.padding(.horizontal)

www.bigmountainstudio.com 184 Free Sample (Get the Full Version Here)


TextField

Custom Composition
@State private var textFieldData = ""
...
VStack {
HStack {
Image(systemName: "magnifyingglass").foregroundColor(.gray)
TextField("first name", text: $textFieldData)
Image(systemName: "slider.horizontal.3")
}
Divider()
}
.padding()

HStack {
Image(systemName: "envelope")
.foregroundColor(.gray).font(.headline)
TextField("email address", text: $textFieldData)
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
.padding()

HStack {
TextField("country", text: $textFieldData)
Button(action: {}) {
Image(systemName: "chevron.right").padding(.horizontal)
}
.accentColor(.orange)
}
.padding()
.overlay(Capsule().stroke(Color.gray, lineWidth: 1))
.padding()

www.bigmountainstudio.com 185 Free Sample (Get the Full Version Here)


Toggle

The Toggle is a switch that can either be on or off. Much like other controls, you need to bind it to a variable. This variable is
passed into the Toggle’s initializer. Then, all you need to do is change this bound variable’s value to change the Toggle’s state
on or off. Or read the bound variable’s value to see what state the Toggle is currently in.

This is a push-out horizontally view.

www.bigmountainstudio.com 186
Toggle

Introduction
@State private var isToggleOn = true
...
Text("The Toggle fills the width of its parent view.")
...
Toggle("Night Mode", isOn: $isToggleOn)
.padding()

Text("Combine images with text")


...
Toggle(isOn: $isToggleOn) {
Text("Night Mode")
Image(systemName: "moon")
}
.padding()

Text("Or you can have nothing")


...
VStack {
Text("Turn Alarm On?")
.foregroundColor(.white)
Toggle("Turn this alarm on", isOn: $isToggleOn)
.labelsHidden() // Hides the label/title
}
.padding(25)
.background(Color.pink)
.cornerRadius(20)
.shadow(color: .gray, radius: 10)

There is not a lot you can do to change the colors of the thumb (round white circle), the on and off
positions. You can create your own custom Toggle. See the chapter on Custom Styling, in the section
ToggleStyle.

www.bigmountainstudio.com 187 Free Sample (Get the Full Version Here)


THE END
I hope you enjoyed this SwiftUI Views Book Sample! Ready for the next step?

Find out how to get the full book!


NOW GET THE FULL BOOK Congratulations on finishing this SwiftUI Jumpstart! You’re now WAY AHEAD of most developers.

ASCEND
Keep climbing and get the most comprehensive visual reference guide on SwiftUI!
The FULL BOOK includes:
Over 500 pages of SwiftUI Find out how to implement action sheets, modals, popovers
Over 350 screenshots showing you what you can do so you and custom popups
can quickly come back and reference the code Master all the layout modifiers including background and
Circular shapes like the circle, capsule and ellipse views overlay layers, scaling, offsets padding and positioning
See the many ways you can use color as views How do you hide the status bar in SwiftUI? Find out!
Discover the different gradients and how you can apply them This is just the tip of the mountain!

www.bigmountainstudio.com 189
YOU ALSO GET…
2 4

1 3 5
www.bigmountainstudio.com 190
IT DOESN’T STOP THERE YOU ALSO GET THE ENTIRE XCODE PROJECT THAT WAS USED TO CREATE THE BOOK!
WHAT’S INCLUDED
Organized like the Book
Quickly find the files you need
THE ENTIRE
XCODE PROJECT!
-Over 460 SwiftUI files-

A $500 Value
FREE with the Book
Yes, this is the entire project
www.bigmountainstudio.com 191
HOW TO GET THE FULL BOOK

Go to https://www.bigmountainstudio.com/swiftui-views-book to learn more and get the full book and Xcode project!

www.bigmountainstudio.com 192
Resources

Hi, I’m Mark Moeykens


I’m a full-time mobile developer with over two decades of programming experience. He has created desktop,
web and mobile apps in many fields including insurance, banking, transportation, health, and sales. Mark also
gives talks on programming and enjoys breaking down complex subjects into simple parts to teach in a practical
and useful manner.

youtube.com/markmoeykens Website: www.bigmountainstudio.com


Find tutorials on iOS topics where I guide you step-by-step through all Join my climber’s camp and see what products I have available, learn
different aspects of development. something new and see what I am working on.

• Subscribe to my newsletter
@BigMtnStudio
• Read articles
Stay up-to-date on what I’m learning and working on. These are the
most real-time updates you will find. • Find courses

• Download books
Facebook Group
Stay up-to-date on releases, whether it is articles, videos, projects, etc.

@BigMtnStudio
Patreon.com/bigmountainstudio Do you prefer hanging out in Instagram? Then follow and get bite-sized
chunks of dev info.
My patrons get the first look at what I’m creating and when it comes
time to making a decision, they are my go-to people.
193
AFFILIATE INFO
I have an affiliate program that anyone can sign up for, whether you bought the book or not. If you mention you like my book with your affiliate link and someone
buys a book, you get:

20% !
If five people buy the book then your book is basically free. Beyond that, you have got yourself some extra spending money.

I love it, sign me up!


Just go to www.bigmountainstudio.com/affiliate and sign up. You will need a PayPal account to get paid.

www.bigmountainstudio.com 194
SHARE THE LOVE
If you like this book and find it useful, I encourage you to share links to my products. I’ll give you some effective sharing
tips on the next page. Here are some tips on what you can an cannot share.

YOU CAN YOU CANNOT


• Share a screenshot of a page from the free book sample with • Share the entire book, whether it’s the free sample or paid
your followers and friends along with a link on where they can version.
get the free or paid book. (https://www.bigmountainstudio.com)
• Sell or resell my products.
• Share favorable comments and goodwill with others. You helped
make the Swift international community super friendly. Let’s • Connect myself or my products with sites that promote general
keep it that way. hate, cyber-bullying, or discrimination of any kind.

www.bigmountainstudio.com 195
SHARING TIPS
If you have never been an affiliate before and want some tips to effectively share my products then read on!

YOU SHOULD YOU SHOULD NOT


• Be honest and genuine with your comments. People can tell • Be pushy or overly aggressive.
when you’re not genuine. • Make people feel wrong or stupid for not buying a product.
• Be specific in your comments. Tell us exactly what you liked, • Be deceptive in any way.
what you learned or what helped you. • Endlessly promote. Mix it in with your other content.
• Share screenshots, gifs or video of your work! • Think you’ll make money by sharing one time. You should
• Help others with what you learned. Add value. casually share regularly. Try with once a week, for example.
• Be open that you are sharing an affiliate link.
• Share more than one time.

www.bigmountainstudio.com 196

You might also like