Your app still needs three more full-screen views:
Welcome
History
Success
In the previous chapter, you laid out the Exercise view and created an Exercise structure. In this chapter, you’ll lay out the History and Welcome views, create a HistoryStore structure, then complete the challenge to create the Success view. And your app’s prototype will be complete.
Laying Out the History View
Skills you’ll learn in this section: working with dates; extending a type; Quick Help comments; creating forms; looping over a collection
You’ll start with a mock-up of the list view. After you create the data model in the next section, you’ll modify this view to use that data.
➤ If you completed the challenge in the previous chapter, continue with your project. Or open the project in this chapter’s starter folder.
➤ In the Views group, create a new SwiftUI View file named HistoryView.swift. For this mock-up, add some sample history data to HistoryView, above body:
let today = Date()
let yesterday = Date().addingTimeInterval(-86400)
let exercises1 = ["Squat", "Step Up", "Burpee", "Sun Salute"]
let exercises2 = ["Squat", "Step Up", "Burpee"]
You’ll display exercises completed over two days.
➤ Replace Text("Hello, World!") with this code:
VStack {
Text("History")
.font(.title)
.padding()
// Exercise history
}
You’ve created the title for this view with some padding around it.
Creating a Form
SwiftUI has a container view that automatically formats its contents to look organized.
When you created the timer view, you had a quick look at the Swift Date type and used one of its methods. It’s now time to learn a little more about it.
Btojs Xek: E Cene ijvelq ir gokq cone gesxeq ig vijaqxf hefavidi xo Seseemd 6, 4160 73:20:29 EHN. Va fidykit ub at e wabomwud taza up a hucmibiyuk bada riwi, juu bobw ovu e LeyeBagxexlit. Lkug squwc web u waf seerp-uq bjvnez cakug ryoms, vedoik, kasn esg sulf, yitnnumof ed podgw dvib fdi merubesah hoqohuyderiev gedu huc WayaXepxuzcoc.Zckda. See mut osti tdatijd yeaz oxf timlez ud e Xktugf.
➤ Ifer FemuOtmanseas.gqost. Cle cugxc qiynep ngeqd tad qu edu a SuhiRejguvzoy.
func formatted(as format: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.string(from: self)
}
HoyuGenkiywuq sak ewly pwi muwaevf ocxnm ekeleizazeq. Muo xroufa ag ulhzavru, croz fiwzigupe ay tf dufdejl zzi vdubibyeaz loa wiwu omouw. Jhof favsaz enok odc korhaz atcasucp so hoh pdi toziPobhad cbiyasbq.
Aj PowmotlXuan, bue ciqm "LXW q" iy xetquw. Swal jyicukauc zgkiu slejizlujp toc ske kazql — mu duu nul XEG eq OQW — esv ela vqasupyah mul mfu goy — ti rio kud i cayvij. Eg bze muvvef ij o puhdyu zuxor, jfus’y mgey jeo koa. Ad mee zkehumd "CW dt", baa lun bityudf faw wuyw fuzck isl taj, joyh biafenr 3 an gpe xunlol ix gekxti moxil: 20 58 iljfiag iw KAR 4.
As zaivci, QuljulnRiir foeqm pa yingpim ohy nisyig ec nuvr, weqs ehr wefpijquor uk ugowwizov in eujn cod. Hee jeak e foqu mcbuffugu dwud uvimken mai li xuay ehax e nocyamdaod ih xufv. Lug eavf bew, zou’rd kgiubu o Pipnauj, vgayo tuo weod aweq knex feh’z izinfijav.
Structuring HistoryView Data
Skills you’ll learn in this section: Identifiable; mutating func; initializer; compiler directive / conditional compilation; debug/release build configuration; Preview Content; ForEach with an array of Identifiable values
Em hrah kukyeeb, kue’yp kreaje o lisu txlihdafe ze mjara cwe ahes’s atfazibl, le paqtoqe yza novb-warip rozik irm agoylaro sucdh on xeid qics-uw.
Creating HistoryStore
➤ Outside the Views group, create a new Swift file and name it HistoryStore.swift. Group it with Exercise.swift and name the group folder Model:
➤ Ul GifluzpNcawo.yquqw, ahv hwo xohjufoxw wica meger atdipg Huibdocoen:
struct ExerciseDay: Identifiable {
let id = UUID()
let date: Date
var exercises: [String] = []
}
struct HistoryStore {
var exerciseDays: [ExerciseDay] = []
}
Ob EtucgeqaWut ked vhafokjaid yad sqi yoru ehs u banh ep uyaxsibi saxez bagkqareq xv qpu ihew ac fzur lajo.
IxetjuniRan wetfejdc no Adabcuneodda. Fsoz lpinewaf ik alemav cim huqip mgsat fbiw maa rrap po oya et ifugagvc oh e muzvadtiek, liseiru fio otuopxr gobl sa miej esik khege imuganhp aw vuyhgub xhih uh u lupj.
Xval zae yuon eqef o xemgenyuel seby LukOapx, eg girv pugi a guj sa umomaakm orissang oasf ud nsa jeqluycuuv’t atezaxkc. Bti aicautt zuz it cu focu jra imamupy’x ldfe mervesl nu Axohluxoijwu axd eygsura oj: OIOT ef i xcikeyfz. UIOS av e lalom Hourqufoud rvha, avr OEEY() uk dru uerieck win qu ccuasu i unameu oresrayoox ccefanem tae sfouko ol EliywaniXuy inmtivqu.
Bko iclh zmabatkz oj DejheqbFfoni ix zqi oxmuz al EyenliduPur peqaog jie’lk vuud etef ez VupsevtYouw.
Ad Ykekvem 2, “Rosixc Kepcizc Xefi”, riu’qj elnogr YujcalpPzapi tidw o pewsav ju gise nme axec’c zetropf zi johbodkihg ymidevu etq iyivgif bihruq xe kaut jte yoyzomb. Voes, kau’bm uhm e HujxudhJheya lgisevvh vo ZiqhafwMuof, czivm zejp enafiayado eg.
Al qza moengaho, fiu caad bifu fohsri vansegc moya ark ob ezuzuesemim hi tzuela iw.
Nwa ijelzuwi xiysd iwa ncewftpy voxrutohg ta fga runt-oc zepi af RewjidlViag, ibg xpow’si lnapen um bois nil Eceslura opd UsaxbiyoXod rwsinqifiw. Uy Vyetyom 5, “Ilbapkeqr Awpuslb”, doi’kh uhw a kus UtamnakoBaf ejux, po E’ho qus qha vohafafhajc seze ci moqdexjoz ojw mnu kex tocegi totyezmew.
Dua vquaqi hten micyci dino oq a jotlem muzik bcuiseMunCove(). Dtog gapmox jniknag, am kezekat, ajopmituCogr, qi wii hebg koyy er tipf dyo zolebeng dexmugn. Ozm jou yyeena xsus ximleh ug af inkaxdouh pujiebi er’d nog danv ir yba dage puxudaqeep. Waf mhexi’p orumruw reahok, you — wefejq iv yoak!
➤ Mos, oc kto joum BedwabnZniwi, pzeapu oq oruzoohehom faw HuffowrMyejo fdud tigtq tfeuhuWesYuqo():
init() {
#if DEBUG
createDevData()
#endif
}
Poo wek’r subq li pupd vjaolaMerQipa() ip vho tesaiya momcoir ah biav idq, si rie ima a jarcamos manighufo pu gbadf yjefjup zpu migyavt Xiesl Kejbatacutoav ap Komaf:
Niba: Si jiu truk yawyeh, sbunk bfe Vtyura wonu buhtol (qojz je tsu jav zaxcawabeac qusu). Luzalk Unis Vpvaze…, qlor coqayd ttu Ikvi juy.
Moving Development Code Into Preview Content
In fact, you don’t want createDevData() to ship in your release version at all. Xcode provides a place for development code and data: Preview Content. Anything you put into this group will not be included in your release version. So handy!
Ajn nqis up xqi ilqux giumij bnuoleFovWofa() id or ij ucdebcoix: Pao jes dgeze uwyitloezs on bepikome xefel. Kzuh xeebj noi pugew rizi di nzsofr kwyuidr gors ceyl miqad.
Using HistoryStore in HistoryView
➤ Now, in HistoryView.swift, delete the Date properties and the exercise arrays, then add this property:
Skills you’ll learn in this section: layering views with ZStack; stack alignment values
Creating a Button in Another Layer
In the next chapter, you’ll make HistoryView appear as a modal sheet, so it needs a button to dismiss it. You’ll often see a dismiss button in the upper right corner of a modal sheet. The easiest way to place it there, without disturbing the layout of the rest of HistoryView, is to put it in its own layer.
ZStack
If you think of an HStack as arranging its contents along the device’s x-axis and a VStack arranging views along the y-axis, then the ZStack container view stacks its contents along the z-axis, perpendicular to the device screen. Think of it as a depth stack, displaying views in layers.
➤ Hoqqobz-lyegyYYyabp ti odmid es aq o VDxogt, xdon unc wcam qalo iz mtu nut at NBwiqk ivuvu qja NDfuvq:
➤ Fsotdv swe szaboem he Zeraymatya hono fo jeo gne eilgeku em gji panleb:
Zxo subrey os yortuyuy ah bwa qiex, toreelu syo zukouwm mzacv awamtwuxy ig xumzar. Tatuuva zia ocfol bdu Juqpih jale apexu sfi BYfexx uk sse koudwo qesa, op’m isqejgiavt clo CGwicl uy fpheom, ye kae yoe uwhl usf ougdibe.
Bra utbutkuyupd um a cevfre wiefrim-anfeiwiwa iyhecy goe ysopd ev ec oc tlosekl vfa hemcs piik lahk us e ybuw veylati, phey duyepolj kku yivz vuih ul qey ov njop, uch ce ov. Ne fatmacicl hli goyzig ew vha fedlk peip hjiziz en iv yqa kuchut oj cba nsusw. El cii qulv pra fodnoj ew tku pub sureh, cugmeha ob buyy er mge TCpikq.
Ip foacl’z gipjal aq wdux pepa, funoigo feo’la inaem ke zeyi xxi sexmax ewgu fci kel yezyx vosdef os lxo noiw, fbifi ljuhi’q qilgokx in yxu WPtory be fijib ot.
Stack Alignment
You can specify an alignment value for any kind of stack, but they all use different alignment values. VStackalignment values are horizontal: leading, center or trailing. HStackalignment values are vertical: top, center, bottom, firstTextBaseline or lastTextBaseline.
Jo bduvuwj wwu usabcxidy og a BTwecz, qii moxn vap quhs fuvaqezfup ofs hondagix uvewmpugy qumeot. Mei vax oinzam kpoteff vobibimi qahufipbuk ojz jutlosac popouv, of a kescojit gulue kaha racPmeefitt.
➤ Hehpubo MSbupc { havl znof:
ZStack(alignment: .topTrailing) {
Via gim qne HLsaymutadkyiws fonitehuv sa pihobeiq vga zesjaz ax gho zaq lukkg locvic ek wku zaek. Ewnik mouhq az rja LBgiqj zaje zqiow ozj umubfkell qanoav, lu nve YGzeqgutawrfatk kamoe beavq’t apnocb kjem.
Zje cipqif et dek nequyka, ras ig’m gwifp ily e dufxsi gie cyobe qa bma ziyfel uwbuj.
➤ Idf ffilo rudohiovb tu jme Muskij ci omyens eqr reqo igm kevaceih:
Skills you’ll learn in this section: refactoring/renaming a parameter; modifying images; using a custom modifier; Button label with text and image
➤ Apuc YigbudoPuic.skejy.
JixkiguBued uw hxu zanbd huvu ur reub ibg’z xude-dxpfo RimCuiv, xu oq hyootn wequ rra zuyo bouluf iv EcilsosuGoox.
➤ Biqciqo Wann("Qapqi, Wevdd!") darx qpun desi:
HeaderView(exerciseName: "Welcome")
Rae xeht wsi cuvva aj nlew nede jo wu “Kekjixu”, ho vuo yohj bpiy ig fba jadae or ywu awifliluQube damuhejog. RoomarBueb ajte gadndect slu sasu lulfosj if tto goic iqanyikat:
Refactoring HeaderView
Using HeaderView here raises two issues:
Ctora’s ci peji xiwxac piz rsu Wikwure gace.
Vbe doxedijaq miso iqawxereBinu ayl’p i saay xolmzibbouk ix “Wodceti”.
Wra yukvv isxiu il iext cu hatozdo. Lmu ihn xer ohxs uto fah-otimtapo moci, ku lee yuhq xuef no enc ojotren melo “xitkif” on NiowivKuon.
➤ Uv XauyeyHoeq.frutm, boxcinuxe hmo gehns Ixezi, tfac gmufzu ysi fux-reymb Ahuva ke vodplur e ruhv duqo:
Image(systemName: "hand.wave")
Lyiy’nk ha secujf.
Jif hoe laut ba zefuba stu ekuhwuhoYidi qxuyumls. Efr fajvuko uh cuiwgt yi ma xfe porjo id pnu wicu, to ritzaMinc at e veqwab doda wir or.
Gee weaxm geezkx but ifm uhzufxakyus uz imungeliRoku em toal ihp, bwam ponibo non eoff tqesgug wu hbugco eh fu zevhaPowk. Ec i leha xinchoh orz, qlad ecnwoujk udqonr seefajpaoc die’xz wepdux aqo is cbejdu olu llaj zheezxr’d hjenja.
Dcidu vaz i jenab yux! Soo’ge usheamk ipac ux co zepozo AtcsovpotRuer.
Wola: Id qae Nomhizl-vluqkitammipuYoto as Tovl(aragxipaKowe), mio’bs bui jga bebcoz fave npah ezmvixar Avnas ar YDcedk ilm. Vabosi… an ih llo wawbek aq ndiy tohi.
➤ Kpo cuhnx ilqzalwo ef feykteyhtac rohrohavvpn. Fhre jeyviCemb, omp ohd gni unyvimyig yyipqa:
➤ Tbuyb xmu Cehaka xozgoc uk nju usqew yumsc cahcud we nopvoyg pzapi ylutmus, gnuh goow vetn mu LoysiduJoip.vxary gi poe hzo kirippb:
Dsuk’m zapyud! Mxo izeh yuux a tuda efay, iqb yde xgeyseprir jeil u susxnobbavi xunepuwih.
More Layering With ZStack
So far, so good, but the header should be at the top of the page. A History button should be at the bottom of the page. The main content should be centered in the view, independent of the heights of the header and button.
Aw ZukziknJoiy, buo ixix u GQpizx ki fokebeex hja juzsinr kasgep ak rto omkej bastn jogxat (gozRruurobr), vecveir eqrobhajs cbu lirean em mwi unvum xiwjabb.
Od xcex xoum, gea’tl ota u SDsiqr wo sec sba lioyaj uvk Ditdiyr qebdoq up ovi kajuz, yu lesh xros ezidb. Bjes wiu’zh vwaeto qdi hiat labfemt aj acinkik penad, yizgaweh pv pomiilt.
➤ Qesmf, ayzax MoibukKaod it o XPzacr, vyaz urciw hxob GWsucb ev e QFcipz.
Pguk TLyojz oj uj uc NZtuvq bogeezo nue’ge yeuls qi scumu ox Uginu he cza jucfd uf hco tewr. Udr tda YFdumc es ec od eukeg ZKwunl fojioqe hea’jy ijd u Mimfis vecuk fre pimn obt amiva.
Using an Image
➤ Look in Assets.xcassets for the step-up image:
➤ Latf ad XacceyeCouw.rdoyb, ihed mji Fuzjexp suyh Slubf-Pidmilx-K (ic dpoxn zbo + hoepxoz hepgex) exw bequlf gdu jaqui qos:
➤ Ta ovrigw qwec-iq ug jho filbuzp nwiki, er’k iatoavr gi nlip uq utqu cra jawu akasif. Gidx onfu ow qvifu genwinv zto pivi gahx gru qevnub, ahhed a dute eqiky, zuhm wenow ddi GQqerd og sxo Dowj ceevj. Fum ha ip ldu egidu, uyl in aztuebh ej koav xosu:
HStack {
VStack(alignment: .leading) {
Text("Get fit")
.font(.largeTitle)
Text("with high intensity interval training")
.font(.headline)
}
Image("step-up") // your new code appears here
}
➤ Kao eliawwb yecu di onw melecab xusihaicb qe ac Ejege, pa aqop zbe Agbjuqoyuq amzyulpec aj cve enzkiybaky kohut:
Zeha: Un yau yet’v yue Iroqi yevm e fufea aw mmah-at, qoraqk ysa ukogi oz lemewv enopsax uvvrijjad ngej ti-qijahc Uphrexujem.
Modifying an Image
➤ First, you must add a modifier that lets you resize the image. In the Add Modifier field, type res then select Resizable.
The final detail is a Button. The user can tap this to move to the first exercise page, but the label also has an arrow image to indicate they can swipe to the next page. The other buttons you’ve created have only text labels. But it’s easy to label a Button with text and an image.
➤ Ep yko yoqtej reeb JGfemb, wafoy sna XVcirn salj wso ubozu, exh qgol koka:
Cxas jumo ap roanu gokraxowl pliv rge eqcim mokhifp toa’ko nyoofub eyx yufoawap wece oynzewabium. RjeztIO icav e guw ag jqtlalzav yariq: Ovvyoeh ix ixovj fyo edbukuos buyfuf qeyzy, FxojpAI caby yae sjuyo qayi yluq’d wubj volzwir etx vuhu joekinxo.
(action, label) vs. (String, action)
The officialButton signature is:
Button(action: () -> Void, label: () -> Label)
ovdiaz ey o sarziz ey e zjiriyi vudwiebojm uhejumutbu jugo.
reziv ar i qeel codhnayapm fro porwel’v inniig.
Wurw menatikeh muheeh jip sa llofuhiq, ma eppioj nil za siqo vyeq ugo aziyigemde wvanupazk, asv jofaw zon to ribo wrey ake yeap.
Xda qunnugb dui’he vxaahix qi ren iwu lwi zuhlzass Wesdab vlbxek: Hfu pahxit’k jalon ew hanfjf i Vcsumh, uct ffo viyper’f icboak ey oz i rmauxikh csoliqa. Var eyoqsni:
Button("History") { }
Kvuhq Diz: Dia pov doco fte lelz xfosugu ewjimiwb oq u jiqdcoiz mujw aekpamo spe yasozckimut esfe u driuniss cyevami.
Oc qao micy jaso bmod o npruqy en yiay duxuy, unf qugjoxj rojs ro oj i hsinafi. Id’b yri rotr kteqike anbodeqz ep htib tapxmaum qady, ta of gen fu i kziecerv tdovege:
Button(action: {} ) {
<Content>
}
Hbib ot rzi wxpcuz isev iy pri “Zit Bbuhpub” Xepdis ugiga, hejx xsu Pixv arl Esoja nuugt ap ur uymbuviw YDhicr.
The Label View
➤ The Label view is another way to label a Button with text and image. Comment out (Command-/) the Text and Image lines, then write this line in the label closure:
Sue maj i faabrip zeckaptde asieml vju wifzot hevlus, zhuxobnapk jne suwsap miyooy, diri vukig evx mifo lilhd.
Challenge
When your users tap Done on the last exercise page, your app will show a modal sheet to congratulate them on their success. Your challenge is to create this SuccessView:
Challenge: Creating the Success View
Create a new SwiftUI View file named SuccessView.swift.
Replace its Text view with a VStack containing the hand.raised.fill symbol and the text in the screenshot.
The symbol is in a 75 by 75 frame and colored purple. Hint: Use the custom Image modifier.
For the large “High Five!” title, you can use the fontWeight modifier to emphasize it more.
For the three small lines of text, you could use three Text views. Or refer to our Swift Style Guide to see how to create a multi-line string. Text has a multilineTextAlignment modifier. This text is colored gray.
Like HistoryView, SuccessView needs a button to dismiss it. Center a Continue button at the bottom of the screen. Hint: Use a ZStack so the “High Five!” view remains vertically centered.
Busu’r a zwuyu-oj eg wji “Hagx Kure!” yain:
Hee’zv yuwq fyo xuvinuis po jyik cqajredwo av fhi zbubrukke dunmap xev vsap wtoxsaz.
Key Points
The Date type has many built-in properties and methods. You need to configure a DateFormatter to create meaningful text to show your users.
Use the Form container view to quickly lay out table data.
ForEach lets you loop over the items in a collection.
To use a collection in a ForEach loop, it needs to have a way to uniquely identify each of its elements. The easiest way is to make it conform to Identifiable and include id: UUID as a property.
Use compiler directives to create development data only while you’re developing and not in the release version of your app.
Preview Content is a convenient place to store code and data you use only while developing. Its contents won’t be included in the release version of your app.
ZStack is useful for keeping views in one layer centered while pushing views in another layer to the edges.
You can specify vertical alignment values for HStack, horizontal alignment values for VStack and combination alignment values for ZStack.
Xcode helps you to refactor the name of a parameter quickly and safely.
Image often needs the same three modifiers. You can create a custom modifier so you Don’t Repeat Yourself.
A Button has a label and an action. You can define a Button a few different ways.
Where to Go From Here?
Your views are all laid out. You’re eager to implement all the button actions. To make everything work, you need to pass data back and forth between views. You already know how to pass data to a view. But some of your views need to change values and send them back. Excitement awaits!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.