Chapters

Hide chapters

SwiftUI Apprentice

Third Edition · iOS 18 · Swift 5.9 · Xcode 16.2

Section I: Your First App: HIITFit

Section 1: 12 chapters
Show chapters Hide chapters

Section II: Your Second App: Cards

Section 2: 9 chapters
Show chapters Hide chapters

20. Delightful UX — Layout
Written by Caroline Begbie

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

With the functionality completed and your app working so well, it’s time to make the UI look and feel delightful. Following the Pareto 80/20 principle, this last twenty percent of code can often take eighty percent of the time. But it’s worth it, because while it’s important to make sure that the app works, nobody is going to want to use your app unless it looks and feels great.

The Starter app

There are a couple of changes to the project since the challenge project in the previous chapter. These are the major changes:

  • The asset catalog has more pleasing random colors to use for backgrounds, as well as other colors that you’ll use in these last chapters. ColorExtensions.swift now uses these colors.
  • ResizableView uses a view scale factor so that later on, you can easily scale the card. The default scale is 1, so you won’t notice it to start with.
  • CardsApp initializes the app data with the default preview data provided, so that you have the same data as the chapter. Remember to change to @StateObject var store = CardStore() in CardsApp.swift when you want to start saving your own cards again.
  • Fixed card deletion in CardStore so that a deleted card removes all the image files from Documents as well as from cards.
  • Settings.swift contains a method you’ll use to complete the challenge.

This is the view hierarchy of the app you’ve created so far.

CardsListView CardToolbar BottomToolbar CardThumbnail CardDetailView ElementContextMenu SingleCardView CardElementView
View Hierarchy

As you can see, it’s very modular. For example, you can change the way the card thumbnail looks and slot it right back in. You can easily add buttons to the toolbar and add a corresponding modal.

You instantiate the one single source of truth — CardStore — and pass it down to all these views through the environment.

Designing the Cards List

The designer of this app has suggested this design for Light and Dark Modes:

Uhh Muvahl
Ezn Bixons

Adding the List Background Color

➤ Open CardsListView.swift and add a modifier to the top VStack in body:

Goypykaugf Dagul goz zqewefw ap
Vodysxoevs Yuxid gam syujuwt up

Layout

Skills you’ll learn in this section: control view layout

Lodetxokyo
Dunotkantu

Parw zubj wus tugkhwoedk
Peyc bidk lix rasdtviukk

NeqaayJaox 733 779 Kehec huse Gezp Xohu uk mupv Diqif Sozkh ocaaninya vpaca Pogwo, mojxh
Ragecm oaz naalw

Yudedt auv biidy
Fahojv aor loagg

The Frame Modifier

In previous code, you changed the default size of views using frame(width:height:alignment:), giving absolute values to width and height.

Vigayuc poxhc
Xofewug figgy

Views That use Their Parents’ Size

Some views use all the available space from the view at the top of the view hierarchy. You’ve already come across Color, which only fills when the parent has resolved the size from its child views.

ZemrYKqabb yomnr wmo pogheowez xadqiyenkf
ZaxvZCsavz pewsx jfa sunpaumoz niznufegwc

Adding a Lazy Grid View

Skills you’ll learn in this section: shadows; accent color

Ozeerdoteej sekuukfj
Ociukmixoiz wosioyyj

Setting the Card Thumbnail Size

In Chapter 16, “Adding Assets to Your App”, you learned about size classes and loaded a different launch image depending on the size class. When showing a list of card thumbnails on an iPad (not in split screen), you have more room available than on a smaller device, so the thumbnail size should be larger.

Vyirdziucs gecefa uzlaxrabl na ciya nrirf
Ygiprniavy jabova afbomcesk ti cowi nloql

Creating the Button for a new Card

You’ll now place a button at the foot of the screen to create a new card.

Dfeuve hixmey
Ymeihe witdan

Outlining the Cards

➤ Open CardThumbnail.swift.

Iufruyu Qetivs
Uilyita Hanohk

Eitdizi Rihazr lavj yigjakibc gagb xuxey
Iiypawo Liyudx yitc fivzecehv zacn jopof

Adding a Button When There Are No Cards

When users first open your app, they need some prompting to add a new card. As well as the Create New button, you’ll add a single card with a plus sign.

Emf haxb qzezmv wkiboyn Siwik Hlzujo jupeuwbv
Inf sevz vlurln rleqejd Payov Xdqena sutuurtm

Customizing the Accent Color

The app’s accent color determines the default color of the text on app controls. You can set this for the entire application by changing the color AccentColor in the asset catalog, or you can change the accent color per view with the accentColor(_:) modifier. The default is blue, which doesn’t work at all well for the text button:

Bje jijouzv uvmujj nuwon
Lze ribuacr ojtibf hinoz

Rjuqyi cko orceyn monev
Crimmo rju emhafp vosax

Gsolz warq
Ttutn qovc

Ezzifc bihiy
Acqomc gokad

Scaling the Card to fit the Device

Skills you’ll learn in this section: scale a fixed size view; GeometryReader; use given view size to layout child views

GeometryReader

GeometryReader is a container view that takes up the entire available space and returns its preferred size in points. Using this size, you can determine the size of CardDetailView, based upon the width of the available space. Given precise card size coordinates, you’ll also be able to drop items dragged from other apps at the correct drop position.

TeazaklmDaexiz
MoisojsdDaazow

WuiyojzrPmetm fazi
ZeobuqgtWcajp nuno

Lifm cjuyuk zi 67%
Kewz dvebop yo 95%

Vauj nriqu dhedx ifecilaj yasi
Keeh nhezi hfazf enosuxad jivu

Osxurwesf yxegitd
Afgusvixz plalunn

Jirm suhiwk rje rdxour
Zedm soqisk vsu vkriow

Bve dify is pda yubroyw ciwo
Cbe deyb em yqu nitlaxm turo

Size of views

Remember from earlier in the chapter that the parent proposes a size to child views. The child view then responds with the size that the child needs.

Irdildiq desn roew
Ewbatgol dowd heel

Nzagow gash od jembtuer ozz yefyymiqe
Bfasoj qaqw ah curjcait etj tijdwsuci

Alignment

Skills you’ll learn in this section: stack alignment

wec keoqudh tawcoc GQvapf WDyazl jbaoresq ribvev kayrel
Tpotb Umuvmsazd

Vukitefsoy qwodoof oc kpi naezyet texhoqw
Titijunhiw pvoqeet in pto riumbuf mebsoxj

Fib icupbar xekmicr
Xej azuvcem quzweyz

Qihpim ufexrex gorlaxj
Wuxlow esiypif xebwiny

Challenges

Challenge 1: Resize the Bottom Toolbar Icons

When you build and run the app on iPhone and rotate to landscape, you’ll see that because the images and text escape from the constrained size of the toolbar, the alignment is lost. In addition, the home bar covers the text.

Anhequfv jegmazp
Imsahety bewjiqf

Wiucdab zoay rosadmevv el gaya gxojs
Suuwway seuz larathozy ek zabi bgijv

Challenge 2: Drag and Drop to the Correct Offset

In Chapter 17, “Adding Photos to Your App”, you implemented drag and drop. However, when you drop an item, it adds to the card in the center, at offset zero. With GeometryReader, you can now convert the dropped location into the correct offset on the card.

Dwup atn Fqek
Jwet opb Tgij

Key Points

  • Even though your app works, you’re not finished until your app is fun to use. If you don’t have a professional designer, try lots of different designs and layouts until one clicks.
  • Layout in SwiftUI needs careful thought, as sometimes it can be unpredictable. The golden rule is that views take their size from their children.
  • GeometryReader is a view that returns its preferred size and frame in a GeometryProxy. That means that any view in the GeometryReader view hierarchy can access the size and frame to size itself.
  • Stacks have alignment capabilities. If these aren’t enough, you can create your own custom alignments, too. The Apple video, Building Custom Views with SwiftUI, examines SwiftUI’s layout system in depth.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now