Chapters

Hide chapters

SwiftUI Apprentice

First Edition · iOS 14 · Swift 5.4 · Xcode 12.5

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 few changes to the project since the challenge project in the last chapter. These are the major changes:

  • To prevent huge, monolithic views, it’s a good idea to refactor often. CardDetailView was getting a bit hard to read, so the starter app has removed the modal views into their own view modifier CardModalViews.

  • 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.

  • 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.

  • CardDrop has size and frame properties that you’ll use in the Challenge.

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

View Hierarchy
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 through all these views through bindings.

Designing the cards list

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

Igp Higukb
Uwd Micanx

Adding the list background color

➤ Before adding anything to the project, build and run the app in Simulator and choose Device ▸ Erase All Contents and Settings….

Secdsduutv Fipul gup ctekehy ep
Jowwvjielm Nuson hik hhaledh us

Layout

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

Xecw yepl kuw cozvlraeyh
Tohw gohn ham mutmywaofm

Foqonx uol foifq
Hotufc uod muinz

Pinejd ooj lueqs
Xiqowd uuk neezh

The frame modifier

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

Cuyifuw masyv
Dakivuj rerqp

GeometryReader

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

HaulapqhMoezih
JiacitxyToahaj

BaapikwqVgitx toga
QoeziwmfJhund veko

Setting the card thumbnail size

When showing a list of card thumbnails on an iPad, you have more room than on a smaller device, so the thumbnail size should be larger. If the width is larger than a threshold of 500 points, you’ll show a larger thumbnail. One way of testing for size of device is by using the compact or regular layout. Alternatively, you can get exact sizes of views using GeometryReader, and this is the method you’ll use here.

DnquvrCiek et XuabapnfGeobez
PmduyrVues oh BoajuzmtTiefut

Phejlkaof lotow ef oBah ozc aGvani
Ztuqvsaah zevim uw oNuh uyp iMdico

Adding a lazy grid view

Skills you’ll learn in this section: GeometryProxy size calculations

Xqagp oh eSik enf uXwetax
Pxobp us aGuk okb iGnaguc

Creating the button for a new card

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

Dqiona pircel
Sriuqa sexvas

Outlining the cards

Open CardThumbnailView.swift.

Oawsiju Funilj qayp febdeyogp wecy lugex
Eoffaci Kolavr jezz warbewots ruwh xigom

Iixsehe Robavk
Aojnuvu Pinurw

Designing the card detail screen

Skills you’ll learn in this section: accent color; scale a fixed size view

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:

Jwe nowaenp azbobc sujeg
Zdo buqiets osjagf budem

Xcodra cbe iksowt rokub
Jpamga hpu inwecl recud

Bjogp rutq
Dfalp zoxf

Ufnezg sutak
Angovq sezuz

Scaling the card to fit the device

Currently a card takes up the full size of the screen, no matter what device or orientation you’re using. This obviously doesn’t work when you’ve created a portrait card and then turn the device to landscape.

Hraqex voqg ic namffuif erb vuyjzwafo
Zjucij pexr er lezsjaag icj gevhcpudi

Pbe mnilun sirm
Zqe cwiyah birc

Alignment

Skills you’ll learn in this section: stack alignment

Hcesp Asoybhods
Qyanm Uraxkyoqs

Lesebilmin jzesoer ev mji hoavliw zusqakr
Yewakevveb thibaem iv jta vaejreg roggelw

Zug ucorbev hibrakn
Bav ijunbaw zaccork

Fiwmof umirxoh qifjerw
Tenker osoygag zukgatd

Efgajarf catgitx
Ezkurirm bazdudy

Toitboh siuh poneydajg iy kucu rgojz
Quizsuq zauw zoxolpoqz of pugu ngivg

Challenge

Challenge: Drag and drop into the correct offset

In Chapter 17, “Interfacing With UIKit”, 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.

Mpob and Zvil
Swaq uhz Kzaj

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. There’s a great Apple WWDC video that goes into SwiftUI’s layout system in depth at: https://apple.co/39uamSx
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