0% found this document useful (0 votes)
17 views189 pages

237 Building Custom Views With Swiftui

This document discusses layout basics in SwiftUI. It explains that the layout procedure involves the parent proposing a size for the child, the child choosing its own size, and the parent placing the child in its coordinate space. It provides examples of adding padding, backgrounds, and aspect ratios to views. The key points covered are that SwiftUI uses an implicit layout system where parent views dictate size and placement of child views.
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)
17 views189 pages

237 Building Custom Views With Swiftui

This document discusses layout basics in SwiftUI. It explains that the layout procedure involves the parent proposing a size for the child, the child choosing its own size, and the parent placing the child in its coordinate space. It provides examples of adding padding, backgrounds, and aspect ratios to views. The key points covered are that SwiftUI uses an implicit layout system where parent views dictate size and placement of child views.
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/ 189

#WWDC19

Building Custom Views in SwiftUI


Graphic effects and layout

Dave Abrahams
John Harper

© 2019 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
} Hello World
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
.edgesIgnoringSafeArea(.all)
} Hello World
}
Root View

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView

Text
Layout Basics

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView
Text
1. Parent Proposes Size for Child

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}

Root View

ContentView
Text
2. Child Chooses Its Own Size

struct ContentView : View {


var body: some View {
Text("Hello World") Hello World
}
}

Root View

ContentView
Text
3. Parent Places Child in Parent’s Coordinate Space

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Hello World
Root View

ContentView
Text
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
Layout Procedure

1. Parent proposes a size for child 50


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
10

struct TrippyBadge : View {


var body: some View {
Image("ColorWash")
Frame
.frame(width: 50, height: 10)
}
Resizable Image
}
Layout Procedure

1. Parent proposes a size for child 50


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
10

struct TrippyBadge : View {


var body: some View {
Image("ColorWash")
Frame
.frame(width: 50, height: 10)
}
Resizable Image
}
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space

struct WedgeChart : View {


var body: some View {
Image("Wedges")
Aspect Ratio View
.aspectRatio(1)
}
Resizable Image
}
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space

struct WedgeChart : View {


var body: some View {
Image("Wedges")
Aspect Ratio View
.aspectRatio(1)
}
Resizable Image
}
Layout Procedure

1. Parent proposes a size for child Hello World


2. Child chooses its own size
3. Parent places child in parent’s coordinate space

struct ContentView : View {


var body: some View {
Text("Hello World")
Root View
}
}
Text
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
4. SwiftUI rounds coordinates to nearest pixel
Layout Procedure

1. Parent proposes a size for child


2. Child chooses its own size
3. Parent places child in parent’s coordinate space
4. SwiftUI rounds coordinates to nearest pixel
Hello World

struct ContentView : View {


var body: some View {
Text("Hello World")
}
}
Root View
Hello World

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
}
}
Root View
Avocado Toast

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.background(Color.green)
}
Root View
Avocado Toast
}

Background Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.background(Color.green)
}
Root View
Avocado Toast
}

Background

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.background(Color.green)
}
Root View
Avocado Toast
}

Background

Text Color
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding()
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Text Color


Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding()
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding([.leading, .trailing])
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast 10 10

10

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

10
Text
Delicious Avocado Toast 10 10

10

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

10
Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast") 10 10
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background 10

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Delicious Avocado Toast

struct Toast : View {


var body: some View {
Text("Avocado Toast")
.padding(10)
.background(Color.green)
Root View
Avocado Toast
}
}
Background

Padding Color

Text
Scrumptious Avocado

struct Avocado : View {


var body: some View {
Image("20x20_avocado")
20
}
} 20
Root View

Image
Scrumptious Avocado

struct Avocado : View {


var body: some View {
Image("20x20_avocado")
20
}
} 20
Root View

Frame Image
Scrumptious Avocado

struct Avocado : View {


var body: some View {
Image("20x20_avocado")
20
.frame(width: 30, height: 30)
} 20
Root View
}

Frame

Image
Scrumptious Avocado

struct Avocado : View {


var body: some View {
Image("20x20_avocado") 30
.frame(width: 30, height: 30)
} 30
Root View
}

Frame

Image

This Is as It Should Be
There’s no wrong way to do it
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack {
VStack {
★★★★★
5 stars
Avocado Toast
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…

Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
★★★★★
5 stars
Avocado Toast
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…

Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
★★★★★
5 stars
Avocado Toast
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…

Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Avocado Toast 🥑
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★ Avocado Toast 🥑
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★ Avocado Toast
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title) Avocado Toast
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title) Avocado Toast
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title) Avocado Toast
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title) Avocado Toast
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}.font(.caption) ★★★★★
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title) Avocado Toast
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

VStack {
ListCell()
Toggle(isOn: $on) { ... }
}
★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

VStack {
ListCell()
Toggle(isOn: $on) { ... }
}
★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

VStack(spacing: 4) {
ListCell()
Toggle(isOn: $on) { ... }
}
★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
HStack and VStack

★★★★★
5 stars
Avocado Toast
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
‫‪HStack and VStack‬‬

‫خبزة محمصة مع‬ ‫★★★★★‬


‫نجوم ‪5‬‬
‫املكونات‪ :‬أفوكادو‪ ،‬زبدة لوز‪ ،‬خبر‪ ،‬رقائق فلفل أحمر‬
How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Deli… Avocado…
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack { S₀ S₁
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

S₀ + S₁

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

w
HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

w
HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack { w

Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

w
HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack { S₀ S₁

Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack(alignment: .center) {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1) Parent

HStack

Text Image Text


How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado T…
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1)
How Stacks Work

HStack {
Text("Delicious")
Delicious Avocado T…
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1)
How Stacks Work

HStack {
Text("Delicious")
Deli… Avocado…
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1)
How Stacks Work

HStack {
Text("Delicious")
… Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast")
}
.lineLimit(1)
Layout Priority

HStack {
Text("Delicious")
… Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)
Layout Priority

w₀+w₁

HStack { w₀
Text("Delicious")
… Avocado Toast
Image("20x20_avocado")
w₁
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)
Alignments

HStack(alignment: .center) {
Text("Delicious")
Delicious Avocado Toast
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)
Alignments

HStack(alignment: .bottom) {
Text("Delicious")
Image("20x20_avocado")
Delicious Avocado Toast
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)
Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Delicious Avocado Toast
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)
Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .bottom) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .lastTextBaseline) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .lastTextBaseline) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

Delicious Avocado Toast


Alignments

HStack(alignment: .lastTextBaseline) {
Text("Delicious").font(.caption)
Image("20x20_avocado")
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

87.4%

Delicious Avocado Toast


Alignments

HStack(alignment: .lastTextBaseline) {
Text("Delicious").font(.caption)
Image("20x20_avocado").alignmentGuide(.lastTextBaseline) { d in d[.bottom] * 0.927 }
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

87.4%

Delicious Avocado Toast


Alignments

HStack(alignment: .lastTextBaseline) {
Text("Delicious").font(.caption)
Image("20x20_avocado").alignmentGuide(.lastTextBaseline) { d in d[.bottom] * 0.927 }
Text("Avocado Toast").layoutPriority(1)
}
.lineLimit(1)

87.4%

Delicious Avocado Toast


Alignments

★★★★★
Avocado Toast
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Alignments

★★★★★
Avocado Toast
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Alignments

★★★★★
Avocado Toast
5 stars
Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Alignments

★★★★★ Avocado Toast


5 stars Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Avocado Toast
HStack {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .center) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Defining a New Vertical Alignment

extension VerticalAlignment {
private enum MidStarAndTitle : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static let midStarAndTitle = VerticalAlignment(MidStarAndTitle.self)
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}

Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")


.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars
Text("★★★★★") Ingredients: Avocado, Almond Butter, Bread, Red Pep…
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
.font(.caption).lineLimit(1)
}
}
Avocado Toast
HStack(alignment: .midStarAndTitle) {
★★★★★
VStack {
5 stars Ingredients: Avocado, Almond Butter, Bread, Red Pep…
Text("★★★★★")
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Text("5 stars")
}.font(.caption)

VStack(alignment: .leading) {
HStack {
Text("Avocado Toast").font(.title)
.alignmentGuide(.midStarAndTitle) { d in d[.bottom] / 2 }
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
.font(.caption).lineLimit(1)
}
}

Graphics in SwiftUI

John Harper
First Steps

struct RedCircle: View {


var body: some View {

}
}
First Steps

struct RedCircle: View {


var body: some View {
// return a filled circle.
Circle().fill(Color.red)
}
}
First Steps

struct RedCircle: View {


var body: some View {
// return a filled circle.
Circle().fill(Color.red)
}
}
Drawing ↔ Views
Drawing Model

shape.op(style) → view

Circle() Capsule() Ellipse()


Drawing Model

shape.op(style) → view

Circle().fill(red) Capsule() Ellipse()


Drawing Model

shape.op(style) → view

Circle().fill(…) Capsule().stroke(red, lineWidth: 20) Ellipse()


Drawing Model

shape.op(style) → view

Circle().fill(…) Capsule().stroke(…) Ellipse().strokeBorder(red, style: …)


Shape Styles

Styles are ways of making a view from a shape


• Colors

• Tiled images

• Gradients: Linear, radial, angular (conic)


Gradients

struct GradientView: View {


var body: some View {
let spectrum = Gradient(colors: [
.red, .yellow, .green, .cyan, .blue, .purple, .red
])

0 1

}
}
Gradients

struct GradientView: View {


var body: some View {
1 0
let spectrum = Gradient(colors: [
.red, .yellow, .green, .cyan, .blue, .purple, .red -90°
])

let conic = AngularGradient(gradient: spectrum,


center: .center, angle: .degrees(-90))

}
}
Gradients

struct GradientView: View {


var body: some View {
let spectrum = Gradient(colors: [
.red, .yellow, .green, .cyan, .blue, .purple, .red
])

let conic = AngularGradient(gradient: spectrum,


center: .center, angle: .degrees(-90))

return Circle().fill(conic)
}
}
Gradients

struct GradientView: View {


var body: some View {
let spectrum = Gradient(colors: [
.red, .yellow, .green, .cyan, .blue, .purple, .red
])

let conic = AngularGradient(gradient: spectrum,


center: .center, angle: .degrees(-90))

return Circle().strokeBorder(conic, lineWidth: 50)


}
}
Sample Control
Sample Control
Sample Control

width

// Data model.
class Ring {
struct Wedge {
var width: Double depth
var depth: Double
var hue: Double
}

var wedges: [Int: Wedge]


var wedgeIDs: [Int]
}
Sample Control
Sample Control
Sample Control
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()

return p
}
}
r1

a1°

r0
a0°

struct WedgeShape: Shape { cen

var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)
p.addArc(center: g.cen, radius: g.r1, startAngle: g.a1, endAngle: g.a0, clockwise: true)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)
p.addArc(center: g.cen, radius: g.r1, startAngle: g.a1, endAngle: g.a0, clockwise: true)

return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)
p.addArc(center: g.cen, radius: g.r1, startAngle: g.a1, endAngle: g.a0, clockwise: true)
p.closeSubpath()
return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path {


var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)
p.addArc(center: g.cen, radius: g.r1, startAngle: g.a1, endAngle: g.a0, clockwise: true)
p.closeSubpath()
return p
}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path { … }

var animatableData: … {

}
}
struct WedgeShape: Shape {
var wedge: Ring.Wedge

func path(in rect: CGRect) -> Path { … }

var animatableData: Ring.Wedge.AnimatableData {


get { return wedge.animatableData }
set { wedge.animatableData = newValue }
}
}
Multi-Part Drawing
Multi-Part Drawing
Multi-Part Drawing
struct RingView: View {
@EnvironmentObject var ring: Ring

var body: some View {


ZStack {

}
}
}
struct RingView: View {
@EnvironmentObject var ring: Ring

var body: some View {


ZStack {
ForEach(ring.wedgeIDs) { id in
WedgeView(self.ring.wedges[id]!)

}
}
}
}
struct RingView: View {
@EnvironmentObject var ring: Ring

var body: some View {


ZStack {
ForEach(ring.wedgeIDs) { id in
WedgeView(self.ring.wedges[id]!)
.tapAction { withAnimation { self.ring.deleteWedge(id: id) } }

}
}
}
}
struct RingView: View {
@EnvironmentObject var ring: Ring

var body: some View {


ZStack {
ForEach(ring.wedgeIDs) { id in
WedgeView(self.ring.wedges[id]!)
.tapAction { withAnimation { self.ring.deleteWedge(id: id) } }
.transition(scaleAndFade)
}
}
}
}
Custom Transitions

View Inserted

View Idle

View Removed
Custom Transitions

View Inserted
Animation

View Idle

Animation
View Removed
Custom Transitions
isActive: true

isActive: false
isActive: true

isActive: false
struct ScaleAndFade: ViewModifier {
var isActive: Bool

func body(content: Content) -> some View {


return content

}
}
isActive: true

isActive: false
struct ScaleAndFade: ViewModifier {
var isActive: Bool

func body(content: Content) -> some View {


return content
.scaleEffect(isActive ? 0.1 : 1) // scale: 10% or 100%
.opacity(isActive ? 0 : 1) // opacity: 0% or 100%
}
}
isActive: true

isActive: false
struct ScaleAndFade: ViewModifier {
var isActive: Bool

func body(content: Content) -> some View { … }


}

let scaleAndFade = AnyTransition.modifier(


active: ScaleAndFade(isActive: true),
identity: ScaleAndFade(isActive: false))

Demo
Graphic Effects

Opacity

Geometry (scales, rotations, etc.)

Color effects (contrast, brightness, hue rotate, monochrome, etc.)

Blur effect

Drop shadow effect

Clipping, masking

Compositing, groups, blend modes


SwiftUI’s Unified Model

All customization centers on View


• Layout

• Graphics

• Animation and transitions

• Interaction

Combine these to produce delightful results!


More Information
developer.apple.com/wwdc19/237

SwiftUI Lab Friday, 11:00

You might also like