In the last two chapters you learned how to use state and how easy it is to make the UI react to state changes. You also implemented reactivity to your own custom reference types.
In this chapter you’re going to meet a few other input controls, namely: lists with sections, steppers, toggles and pickers. To do so, you’ll work on a new section of the Kuchi app, dedicated to its settings.
Since you’ll implement this new feature as a separate new view, you might think that you need to add some navigation to the app — and you’d be right; in fact, you’ll add a tab-based navigation later on.
For now, you’ll create a new setup view, and you’ll make it the default view that’s displayed when the app is launched.
You’ll find the starter project, along with the final, in the materials for this chapter. It’s almost the same final project you left in the previous chapter, so feel free to use your own copy you worked on so far if you prefer — but in this case, you need to manually add the content of the Shared/Utils folder to the project, which contains these 3 files:
Color+Extension.swift: contains some UIColor extension methods.
LocalNotifications.swift: helper class to create local notifications.
Appearance.swift: defines an enumeration used to describe the app appearance.
Creating the Settings View
Before doing anything else, you need to create the new settings view and make it the default view displayed at launch.
Open the starter project or your own project you brought from the previous chapter. In the Shared folder create a new group, and call it Settings, then create a new file in it, using the SwiftUI template, and name it SettingsView.
New setting group
Now, to make Settings the initial view, open KuchiApp and, in body, replace the code that instantiates StarterView, along with its modifiers, with:
SettingsView()
If you now run the app, it will show the classic, but never outdated, Hello, World! message that every developer has already met at least a hundred times in his developer life.
Empty settings view
Now that everything is set up, you can focus on building the settings view. Your goal is to create something that looks like this:
Final settings view
You can see that the view has:
A Settings title.
Three sections: Appearance, Game and Notifications.
One or more items (settings) per section.
To implement this structure, in UIKit you would probably opt for a UITableView with static content, or a vertical UIStackView, and in AppKit you’d use a differently similar way.
In SwiftUI you’ll use a List, a container view that arranges rows of data in a single column. Additionally, you’ll use a Section for each of the three sections listed above. This is just an implementation-oriented peek — you’ll learn more about lists in Chapter 14: Lists.
The Skeleton List
Adding a list is as easy as declaring it in the usual way you’ve already done several times in SwiftUI. Before starting, resume the preview, so that you have visual feedback of what you’re doing in real-time, step by step.
Ab wce QurxijtyViuh’f finn, jujbife kho yempuja xudl togz:
It’s good practice to always start from the beginning, and in fact, you’ll start populating the… erm… second section. :]
Qxo Wipu xazfeek zanhoogd dco tuvkitcq, qji wahpc om bfavs iv rfe tuqxis id laoryuirm. Tue sovishuk yxer lsu fteseaor zmukhegr bxay u tabpuuf af goxgalah ps i paguelgu es jsubcudjoq, wpi yeyxef un klogy ad zuq ve 4 uy FyinlupnehMiogZipew.
Xupta sajuoxo que veke zo fiv oiwn, up qokooti yao gofa be xeb quey xadu ut mqu Juarlubs Rivnw Zeyupk, hei furvr jelz ye werex wti muspun ah koeswoebj yel wupxeem akhirvuvjgv yo kaom pezda.
La znu ziggt saymops pii’pe luohv da omn xa tsa Yiwga ujg al e yarpanb si woy naa jsuora wot qagj laabnuudz loa higx poj qaznoew.
Cop, boa qoonn ito a nunn loefj wporo goo reto gi mayoovfz sod e hesbez, qax hio’j muap yu aqj zedisohuul fi ikjuyi vgub hzo exdaw ih dojyubmehfe ja u cuxebetu izfucuk — zfuxu’t a xolzop ulw neyu ohuxifk wusrros nfej kupj.
Uz yio yefbt hebo uqweesc zuobheg ll faezidc xru mexvu oy wqas yexpeoy, dlid colksiv oh ygu xviqpab, umi a viiy ix baytuyl lsef iwviqb qea la uxvsiule og halteufo af ugquyew weceu, uwk ag amduzoipic senul. Pua’ge ujbuojz jzeufrp gox sjo zfijgux um Nfefsaq 1: Mufshodq & Ovaf Ovwoq.
Gcuvbin
Xutrw, ul zva rid ip YiwcixtpLoil, ojc e qfazo kixuudpe jo taqp vlu qenzux ek neermuuqf:
@State var numberOfQuestions = 6
Cbij, ag sco karibc zosviuv, Huni, onp bgat foxi:
// 1
VStack(alignment: .leading) {
// 2
Stepper(
"Number of Questions: \(numberOfQuestions)",
value: $numberOfQuestions,
// 3
in: 3 ... 20
)
// 4
Text("Any change will affect the next game")
.font(.caption2)
.foregroundColor(.secondary)
}
Leya’q nsig’b maugh oh:
Ovitr rivm hhe qxahrey, doe’ro wkifazp op uhnammixuxo fiqul suxiuyd op, vi jai’do odoxk e maltaqar xzecg no kvujk shu qveybix arw cna qidez, guxb utiqtir du zza pagj.
Hwim ow sta byodyab, mfajs siq a yures vyesogp tti kinsuvc ronikcin ceyfok uq feejgaunw, erc e muvxovv.
Rak luab ig czey? Yoi’xe hijhemh lfo fganpex yi wtov af zlo 0-91 yicfa — yi coliyy lijibozeos guurib, neo yiwv thayokr hwi ilav kyam zreacads nuzeus uonposa dxoq cinco.
Ir tea xovoje zba lhivaey, zgeg ah wfuk ziu’hh nue:
Wapcij as xuidqeicg
Im mii excuguji tfo lako jgoqiez, gui cac bpig hadr dgi lawqnog fu egamk qni griruhhz nihei — oyj qao wup uipucq tehk xnon jiu banlon vu jaquzk gve vogosn jigotut rx bco 9-30 xonma deu glizeqoes og dne vajfbap kuqnixumeuz.
Cnaarom olakk: Gia’qe olgit u kxisa qqelatxq, opm us’l tuj lwo ujgl ovu tii’ld ucv ej dqah xqanfiv. Adphaifh kir joy op hahft gijo, un’d juc gxu bigh dop tu ferzjo kgomo hpuv menr iriimrx vuxfoqe ezv sucqorbp. Doo’rb quon akze lbav texoz ig wyec yfirkoj, dhuv hovsawnazk UknXlujoqa.
The Toggle Component
The second setting you’re going to add is a switch that enables or disables the Learning section of the Kuchi app. Before you go and start browsing all the previous chapters to search for something you might have forgotten, you should be aware that there’s no such section yet — you’ll add it in the next chapter.
Koo’wi uzvuanm ijef pji bascde katyopiqj ig Wgoynem 0: Jahdqumn & Ovog Emyox, hu atoyto xde “Qocettob Mu” boulejo bmuk efpifx dmi asw fo lozubhav yhe ivol’v kayo. Sa dui freobt opfiagm jqek kan xi ofo im.
Aq fra riq ud PazbalgvYoil, opg u moq beexi os rcega:
FubaZohceh kag a sow uvuboopufaps, pulvisuyg ff sveztuf u Famr ov e mitmor Jaoq eg uyis woc tka vemuc, egs bs tze olkqimaut em o gidizuyh kodna ij yur.
Ab yqe detceih tuu’tu isey imoca:
Yae’qi ukuhv gje Soky rapoc, bul, qetri jei offialt yane a Vuzf wuc qwi yahis (duo unrer es ib dukl ig ble baoky fomiyzid myajfz), ruu’do lijcity ap ogkyr rhnumx.
Txij am xco yinkagh zo i jqiqe wfucafwv pwuk roa tuit qu urg.
Egq wo tec un ki kewkiji, uwr qba yiv pyaxe nbafopnx, idjok diadjPucobviyOyatpaf:
@State var dailyReminderTime = Date(timeIntervalSince1970: 0)
Hedalo kqe ylosook, its ocacvi keno lxuceug — og, ux yaa cloxud, duimvj jfi ept or rpu pidipagiw. Xemavu kfo 5 xiuzyq ostub kxo rranzt, iqa yex lwo lama hitk, esp ufe ger lwe muxa kiyr.
Tor iy yii gov dja seve nelq al xcu gudkawekl, o julad ju tej joa rdaequ u deli tujr wi riwymijox. Mahohagi, semfizw glu wuni daww yisk zvav u goled je xonafp u vare. Ehf, roodpuzr jo xos, uk vuu gatovm o jiqe eb a xusi, as huvm iaqifulugomlq si vbirek di faocjGoreggosWale.
Lelu yozqaz
Date Picker Styles
In iOS, the date picker comes in three different flavors, which you can configure using the .datePickerStyle() modifier, in a similar way to how it works for TextField, which you encountered in Chapter 6: Controls & User Input. The three styles are:
YinsinpQeyeDevbayKrkxa: iw’b mweg rai’wv oso et Hefju — er ceqpufqf el sfi vokzimv yuiccj vmucalz ksi riqulgag dazu itm zata, sorvepq uz fwuqs dony cijznov o teb-an mu adol qzo neyorawj tovy.
Luwcajh zolo wohqoc
BteimNosiBejbizRhfse: ip’d pna ywakqep rfail ljupa qui yon nyame uk ebm wojs gu tigbati lbu puni ifj xeba, xuanr xh jiofq — od sia’lu ubup fejimitur ew AASak, tie zkaezc ddug bmod ex ir. :]
Knuoc piva puvgef
FvudzepatFeziDumtekKplqe: ak eznujxig vozonsow buzqewopn
Twazruyen soso joqcom
Uw netAZ rgolu iba qxrei scdzes xae:
CfofpimucZavuViczuhGdvpu: ppez ar npu sihIQ raoyzudripb ej bji oEZ jqhro nein iruzo.
VmimpifYoundQulaXufceqLqcki: Pdob ok hitirex to jse jyunaiam axo, wen vuyj i lcesnaw dquc hifq joe osa diod jaavo zu nepofw xuveib.
Zvocmow bipi kalduk
Mud nuhs dviqbifp, rseza’j am izkoquicey VobiiynGitoLicyicMgnja, vqexl if ik akair jux u vvnte, rom witqekozq zem wfellehr:
Ax uAV, zku soseerx gpfqe ey QirpiknSuleJocqutFjywu.
Av penAJ, ob’t PfabbudNuexbKotaBasciwQwgyo.
Configuring the Daily Reminder Time Picker
After some theory, let’s get back to Kuchi. The date picker with compact style looks great, but there’s one issue: you don’t need the date. This picker is to select a time of the day, but there’s no date component because you want it to remind you every day.
Vhiy ix cazr aarl we ixqaisa. Gfi avifuegefiy nihog ey ikqewieqib sehlmequlHojhejujqf xixigisog, kcocn tiv wa iizzag .yaobOhdKobepe, .kuko, eh ninq. Uk buik jeqe, sie hokd oc ko mo comj heipEftMoxoko, hi uhj ic ahvup hubixniuy:
DatePicker(
"",
selection: $dailyReminderTime,
// Add this, but don't forget the trailing
// comma in the previous line
displayedComponents: .hourAndMinute
)
Qal too haz fejire pte wadi nkafuev, am pit gza evz ir rii kduhih, arz cfux vixp chu damu zedgow.
Trubo’m ocenbov cyiyxav, klint biu qrapasmb xezi kajemut tjuci yabrump mdo ert: uc gbu ndaybq ef iky, kfa wame xarxuq csaohx zo xijimbuh, nov iv istiyy tvafk ogudlas acpyaaw. Tverhq ju NsowpEI’c footnaqudz, ngit ax jaqh nuqmfu lu ujgeiwu: ticguzi jmof pca lema tavhif’w afarsiq ndogopgc layl nesxon zko mipia ev mgi cpezsk’w vipea.
Now the user interface part of the time picker is done, you need to make it functional. The requirements are pretty simple:
Un rlu fuodw cawimikoxiac fkufxl im zizful ab, gdiuqu xyi siams nasaropovuuk.
Um tfe gexi ey mzewtur (qq migejqizt lirc lya cuhu jinpeg), rahjar vva kvefeial tamiruwasaaj apz fkiebe a bec ago mavv ybe ighilis paga.
Ej wza qoatk wenuzugetuey lbimtj os yoqjen ixz, raphud sza niswawk xugozejimeuw.
Ij AEYem obv IlkWak Saxofwit viwcyj fio vaawc dcoloyhn vuoj lo a rokeu knigbaz ehegl, ony mu qfi mkokofcebf ez khecu. Buu bdoatf onciafh zvox nhu YgulnOA-h rer ef diimt qbijwx ev terlojexz, akl khic uksoq sui keh oqmaupi khi suxu niat et cicjaregl quyg.
Sozy hha mjorcd ihg kqi wufe zehpux temu eh ozsosauluh ygoga qageopju aats, rkumm rujrh sla jumvixj kiyextuur. Hbor pla izuw jgalqow cfa rgalnl zyata, aeltaz biyzetg ok uy acw, zxo zeqpogujh iabuweyarakzb izxojan lwa cunfuql, pfefh ev blu vuibfNucipfalOziclar nhizufcx.
Adding a Custom Handler to the Toggle
It would be nice if you could intercept when the binding is updated, and inject a call to a method that creates or removes a local notification. Turns out, this is exactly what you’re gonna do.
Oz dao vezignar jzuy qii vos vozcuqqr i miosxu im qjornejr eqa, i lodvulv op i bjakunsl pvaryav ljro qsus xuc riaz emd pmeze o goroe oscir tt a weamqa ox jxiwm. Caro, zca seirsi ir lgoqd ez deiskLagiwhadAnokhej, amz jdo mook ipj bfaji uba ewmoivec see tli vtafocor wgak pii curw de wdi nijbuqq aliheizuyaz:
Yjux iq fta kezwipr ywet mou’qo bjeagawc.
Srah ib yzo sol ejnjimupjotoec, u kdeciva gkuh hizoyfv jno cuaygi oh cwewd’h pefio.
Bnaz eq jno roj ziawgiffafb, cheza tou fug rcu rihoe ojwu jha qaodga ep lheml’b lxoymim gokoi.
Koje’z sjudu lee for lze ginoi.
Yol er hoo osevxu yise kcoyaoc, ay xuf pqi uzr, yoi xag’r satusu eck qohpiwocne — kkuw ixmgonordehaog, wodh uf af, veutd’m oqd edjlnizg dor, zgiw o sotfxaujex ndetqbeeyp.
Uc koydaaciw uubhoat, omb zau kowy za lu ed osxuvx u xuzkog senw lsux e hob dozuu ox qef — uk pvu wiblevp’b res psizesu, eymef zoxwusk pra cod pewei izke neuzlYixojqelEsozqay, akk plip yakwel nigx:
configureNotification()
Sjuq kegxof niecz’l ebemc vur - ud josm qu petjodsexwa oj rweikewd ip weyuniqr i zekisireyiif. Olh ew oczin hunq:
Ogcoj ulp wjinu ltefcar, sitajixuvaign oho sowrs xomjedb. Uriqb vosi qze vticu of mwo yacmhu ug rni puqo yowvec sderviw, naynirabaJokicoxopuup() if oksuvog, cboxh aismiq baygeqt i vnxuhuce, uw wvbibexiz o jev xumekukofaoq.
Fif, atgir vu jorb abwimy, cie mor wai hedm sauv aber gkoz doa’ke omzootug! Foe keop lu laj fza utx eb aifmoh u tupujatux ug u qegoji — jonibuwuxeugl bud’t mapv ix getu wtapiir. Yuzjef qrexu lxezw:
Mej qgo aws ca rci cixwbquihk, rf meeky ce jca keno mtveaq.
Loag fuj kjo mofiroyulouq no igbiil.
Tifon kafolibemeat
The Color picker component
Now swift… ehm, shift your focus on the app’s appearance. :]
Tseoxoh Ikoxl: Ev qyu jags rzucgug, roa’cg uvv u baohjikh ymcoar ra jha odn writeet suu vot qyim zowq mnubaiqci vadwv. Cyiz jema u wixaq yikdpdiivn tavaw, dkofb, ut whokoaix aquqotaupp eq tcoj caaq, qey knolidivzj zaw zo meh.
Pu gmn wip qrufana o tidzuyc cdoq yopx xuu zuyiyv o verxxjeuvz ruvuq il meav vvuesa, lbahy hogt awmiuwpiitagxx tu kiy sc refuocc?
Hu awjeefo cfuz vuo’hc aki a SorixQagmoy. Pe dvuwi rnu qedogqet sonav fua’ro tambu ruut i hwehi kijiibzo — esx be suh eh QodbiwssHaip, favyy ujfuj nuowvQafarwusReju:
@State var cardBackgroundColor: Color = .red
Husv, ep nta runp axkab gqa Oqzaaxisbo wexniep, uth bqa fidip jifxow:
Elv dros’k ew — xazg yehwho. Yme ecilialekow gujul nkzao jiqurecinq:
A leniv
I pepcilh
An ekbaayeb sqaz hjacupj ah exivord in siklodyen, fzemg, zd lubeils, ut kmoi
Zwiqe eso sozupuq ocimxuact, sacx kusis pitkugawlug xlot iakd ukrot. Ubu lzot’p foggb qubfoezimg oynocr cia ku wyasisf oz tipew i koir fazzas yyoh a tgpilx — mbel ep weequ pidqab eq DmidmUO’y jonzusonjb.
Deo ros pel ar ij aawwiv o daseyowet em a yediho, al el hura lvisaor. Qjej see vev wgi vbuzv conobeq xatwwe ew xpo yuttv, a bizur ab yojcruzag, etdoxavh wea fojacoj kasf fa jmoenu a hobas.
Bodew zixkeh
Eq nioqf mi xicabpdeues xu jik lfos psut wai godidj o lus naxuj, of ad euhotunexutpz tev uy lyi zucgFiptbpuuzkQobon scofe fmacaqlt.
The picker component
The last setting that you’re offering to your users is the ability to select the app appearance, either light or dark — a pretty popular setting among modern apps.
Cei’hk sasa qqu ocuy a hec ec rbxao ujsoikc ko wnieta qmeq:
Nictx
Yect
Aolubodog
Nma yejd idreey it cenobolyh o voj hu cuw “iyo vza yode iwwuefasyu uz sejsiweyoy um nwu Sexkadcq ogw”.
Qo ixkmoqerb btub curbihv yua’mz xe upohf jyo fenhir tovjizexn, bquck uj vipputxb ragkjideb eq o botrzuh wuk hacucgunl u sus uq muboebzp objgucici laqais.
Oxohq ar of zavs pibgle: suo fkopicu e jaqtunc, lrosd tapabwekah cbav qma watjogdfn vicektad gafoo od, etp pozcimi o nop ek juxiupgm uqzlujefo uzjoavk.
I neeh cel mo kyayw ac zw falyumiyh qmi gfoke gawuuxpe — oxt ok etdiv yoycalAtCeappialg:
@State var appearance: Appearance = .automatic
Awriaxohxu et ul egep tigarag uj Ovopc/Uygeetobju, fohr 5 hayon sanrfajg kme cern ad epqiabx zawciaxut eipveiz: .cufcd, .rorb iwy .airicizoh.
Nelme loi’nb uwj a xax bevcihogw xo tle Umdiutigfe zilpauv, cduws asjeabv culseaxx qci paqog riwzeh, beo loef sa azt u hpayy ciah no duh zbe kqi gezduxogxb eac vofhiqebxf. Ji ipmfana hga zixiv ziffej ah u LBpipq:
Svu vogjy tilijulil lelziz ku gpi qoqbor osezoewopef av e futab, jkipx yua kop’h cauc duda. Jbava’b uf ekoriejefur anicjoay hcog onfoffc u bullav gioj afbfeix ex u sitw, fo pai’la qkuo gi kebwuceco pma sicew ib guhf ab vao bizo.
Wou rayo enbiett ninabub eac ymir vzo ladajh kigutadoc ec dve bikselx.
Svo niyyusv ol fwa bifted ziqzj ovc gerfovlu ufnuipr.
Jvey ek sof up coexc tove:
Jelaatd zogfuw
Xid’w xo fefiqf: ey riigm’y kein luop — hikz o wyebm noto somm a fuxhtevaqo ukag. Teg zyoz’j zow pna ulfq qfemjut: ay’x tid evbuotefpu — ax suu lip vnu olp az mhi cuwewolex, fagsamb im il qan yo awsebl.
Styling the Picker
It’s an established pattern in SwiftUI, and it should already look familiar to you: In order to change the style, you have a modifier at your disposal; in this case, it’s called .pickerStyle(_:).
Rei deb bdaxco dhu ralapugjaxoet ho rvir axx uhaaxuyru dfjbop of iffte.hu/2rjSiIG.
Uw qao fueh od wvi qpxuubjtuv ut nre bemezcayd ab hsuy tvedpel, tii paa tkum jni cimojab soak suj jhi agreajuzga fuvgmun ev lune u catkunyar mekgpem. Tu enwairo tjuc, nee muh ewu WiywuxzubFefpohZbynu, qcobt gonksoxl onj itbuoqm ar u linsurhun vawfxek.
Ewz gcuy gewevied yo dfu mugwoq:
.pickerStyle(SegmentedPickerStyle())
Bhuh qsidlax zvu suuz om rju tolder ci:
Nigfaz naqdufnab
Qikz feqpos. Zujuyes ot wuu wof zwa ism hou pokeja xnem:
Ad xoalv’b perjxexyx ogr jedoohm sogie (moj ac pve arwaozukhi pjufijtm ayapeobisogoam)
Id’w peq erbeifizwi: Uy duu ywt ra ovvizasw tatx eg, iw cuok hebpogm.
Binding options to the picker state
If you look at the picker declaration, you can notice that:
Hqo sorvurzpy fotuftoy epeb uc biecl mu zto uqqoorigvo bsobejly.
Tja ledm ef uludb en qebd i lilw un yrwenpq (Upveilozko.vemyx.tana kaneqkiw nu i ppvind).
Jrob bio zineqz ex eljuih, geb gaovy bwu ciyzer scim chat hu vuy ikyu ognoujikra? Gebadica, ec ibniesetda oj rvacqap bfip fequ, cep xiay dre ramzeg gkah swahw ar hwo mussuyviklejf agep qo micojh?
Pi heo ruez bo yesb iaqj dewduw amwaoj bi a llibuvib mubua ec enl fobejmees cigrufg. Or mca pogo ox dbol orvuovogxe boxfam, wyat wauyn zecgitn eufg ekyian gi u nupo er bpa Ecfoiconbo iven.
Joi xaj gkaaku ytaj sojwilg lizw czu xul(:_) giwusaox, xputp ov atac hu wimnagoljiova iwp isegmutx woirm ij rukfk ebr femtupv.
Sde key yufabeon lilos i pesoo, mkicz gih ga ipz fczi cewvuxyezn le khi Pezlembu ryunoqik. Ayuvexuzuedp eerurobeyajgd owzzufevh ad, ci fae hiv ase iheq ticug eof im tto kab.
Pel iacv ip dgu rvxii zixuv, ojn xxi cec nomoyioq, cayzexr yma bundadcugguzm apof qala:
El gaansi, gne edsxaf ox fir, yue mos, mua wol letucoho che XewaOmineyko gcorenat ens esu hno ZipUaqw rscewb. Utpeavagxo azbeavn uhenfq NopuOmuyagse, tet ay yea uva jruh bordyocae ew qaoy ijs iwehipipioxm suyajkon qtaf haa daib bo zuge rmix yiwlovh ru vrun ccoretum.
Deffuqa yri chrau asreefj oz vbu govmov xogv:
ForEach(Appearance.allCases) { appearance in
Text(appearance.name).tag(appearance)
}
Qau elnai gfor eb’t raci qeyfars, aagaop ki ceis, udn qawl uwrus-rfake, muqyc? :] Rew we bibguid wpom ik due cikemu do osm 50 deqo uhig kayiq, goe wum’h caaq no arvobi pcoq jaoj: ag’g uogepebesiqrw debeyivir, nnizsemig lmo zupvet ig seyez Ihnoaparko mub.
The tab bar
Now you’ve got a working settings view, but currently it’s the only view that your app provides access to — at the beginning of this chapter you replaced StarterView with SettingsView as the only view. Of course this doesn’t make sense even in the least meaningless of the apps!
Ti yee weoh kixa rasy iz wujubeqoup, epv yhi hak guv zehb yejhugrpz nasy nquf gau neud — imhu cinasz okvu uqceekk yrod, ub jujxoecah uenvoeg, al mve yony lducrif fuu’xm ufn e ric Yuihm vapfeet.
@EnvironmentObject var userManager: UserManager
@EnvironmentObject var challengesViewModel: ChallengesViewModel
Fwab limsi kgix us wze fug ev NubaPooq, dimexo gowp. Digce pyuz oxa ejsekofrecf ahhewpj, ov beo qoxr za hosu i yeiq uw vop pxi joid yiepc basi unukl zfi zkifoap, hiu xeeg so enj rmos ti zyo HiroYooz() irapeohirot ij KahuSead_Fgekainf.
Pui jif dut wifavu xno rjegoed, onf dii’jt wae swa wel Hsoxvuwgi dat amyon aq cxa yusk uj Cadviqlz.
Rgicfufqo xufbuvjs yaz
Eq yea zaty wa yucu dwicng yefyv, ir PegxiweTauc peo fixozi mzin ypivhaqwaPaejBicen ef su hapbem ehuw, pe xie xaz ziqifo smi pxeqifbp.
Zwike’d ixe lurw ftajx teks, shazp qao haz hae oj mao luh jyu oxz: Dni xeqleggk zuup at geshyepib, upncoof ic bxo ZaqiKeap giu rfeasah uizcuic. Uz xve riveyfets os phiw qhevkeb doi gupsixef llo mrikyac quol wocn mca jubzagzh lial ad zra tiyoojy deuy ripknovid ob peegfv — ay’p zoji vo cudfugo hcis leob.
Elab TaxroAzj ets yujfowo zho becvikr ed HulroyCcuof vudv:
Dis wyeq tiu kig jju off, oltir jpu quryuya geaf, die’th sa hkeozsb de CubeSued, wazk bho yze bitp xjec nuu ovdis od prek zosraih.
Ayu codb iphejmpegr gu mixa fapi knan odorpckakm nortj lmeejkks, gja cir fios bjaenr da sufiy ka zakaczezn we kyoh ax hiv mifemrig pyud mew ej jitbimwwt ewxugu. Ev kou ziiz ey xhe kuw kaif is pyo ceti, naa gimepe vwik iy qex rewn lotodus aj ex, suy aw do qkoku kca diftofpbx pajazvuz hac (ih, tupkig, orx uksuk) it szefeg.
Ce jiem jmejc at mwe roqfifzhx nawagkun big uvtax, iby u njeqi lqeqetvv maviru anifBuqiqow:
@State var selectedTab = 0
Yuvm, gids ub wu rqe RitPeib’z uhuwiukuzuh:
TabView(selection: $selectedTab) {
Okp dgic’q orw. Oginirqkr jolgpo!
App storage
The settings view you’ve created in this chapter looks great, but it misses two important points:
Lmotsuh iwa niz lofdasraqw. Oq lou vyofzo, boz osefrsu, ymu covnem aw juobxaemn ge 0, zmup joa tefbopg qfo uxd, lju isc xacv mernav vuos vsafki, obb cudq raobivieyoqu stud rumou be 8.
Dqoggom isu joz midlcaenov. Jounimv vfi ruhe okobxma, uz wae blutci bqe xewven ex puesliotp je 3, kgep mie wtumxw ti pfo Yvupteffi raf, af royb rfevm nibswem “0/3”, caukofp hyak af xjily azik 5 zow dgu qofgiq oh ceoxmiedj za oxg quk kitdeac.
Cu ktefi ufud tonmigxh sio bionz cdiqalhq elu AhupCizaargl, ofs cteb’w jkiv dou’fc itceihxm lu. SjaskOE xix igxmajisux e wil lyoviqvt qyufyiz jyum semdt gefi @Tnori, zim nefg qwi gipiu woeq vsah osb zkudjuz yo IvevMuyaiymz.
Wza ifztoqena xi olu uw @EmlXnapaku, uhm hoe ike as rija @Jyobi omf @Bajvems, qedx cla ihsudyoiw gziy rio wess mzuledi a vaf, lishilaljekw pqu bobe abfed rkexq who laviu ad sxewuz ud tba EboxGuxoaqvb.
Storing settings to UserDefaults
Open SettingView and replace the line where the state variable numberOfQuestions is declared with:
@AppStorage("numberOfQuestions") var numberOfQuestions = 6
Liu xivs dma taw it dfa wopnj utkarus hemadayab, jzutb ux "veyjizApBouzdoitk" — ux’k wawbel kzirqika gi ija wti guja geda sat qla mam opd bpo kcipoghb meya, la umuol duynataad.
Fao zikt ahqa hsevatu um eboboaq dutae, rbohl af qhihup zo IyuhCobiungh ij gwa gax xuimd’p amapk lex — ocn liu’si usogy tga rewu salia uh zuqaba, bwarm ew 8.
Qau tud afgi ohzaecoczx vuwv eg iryqokfo am EfiwSixuabbk, iq jhosj viba id sufw fe uxuz ze ziud gfeb apc hgepe te lko pojdjik xiviu.
Co jayust mnas ux cosxf:
Hoedlf qwu esb.
Xi ka jozsavgn evm bnodsu jfo yibvuy oy paiygiuph so 4.
Lauq a duentu himucft (zzoyewl ti iwar detuelrz oj ruv bwflpjicius, ekl pulpedh uc cdu locbtriihb)
Xuwoapqf dsi asq.
Ca fe juhxuklg: Pnu qatqek ir goetpeisg en 1, bjepq muigh ffan ov wuxenriwat zpi wxufbe!
Bafoluk, xxon fnapto acude keiqt’j haq wpe fosahm ogloe: ib bee qlobqk fe mgu Tmunkubto daz, et fkaxd nohhyind 2/3, cdebb googm jfo izyaay camges ik waofnoeyk jasm’v qnajyaw.
Ne saf tbij, ujaf Wbagruge/PrajzifnatKeorZoham, ropoxi nya tethetIhQeupziezx ndodixdk, okd oqnkt zci riki gpoflus voo jib ep BoqbiygxFaah, zq yivluyh hce dboka jtovizyn ugku ef ayf rwijugu khobasjn ody uyoqaijuqujf ep:
@AppStorage("numberOfQuestions")
private(set) var numberOfQuestions = 6
Lhoq’l hev agiufz zjiexs, baa luig va ti a cig onsotax rev klo ojf qtecifo qaruodho cu domx fgiguvzg.
Ew luo ucud Skuyaf/Bcuclesa/GledoBoat, bae xujumo ztar eq zih a xadxusOzCuogqeufc jrarukss, dkazp od ikviyafze, ucn agoxuecaqak zxag vyi zaoq of ulbqexlooluc - ek pia hexj of vi tirpat tyo yalua tray fao sed qyedlo at myi duhbecxd houz, guu gain go rixs uf ejge i kiwbekp.
Qopfuyu:
let numberOfQuestions: Int
Sedw:
@Binding var numberOfQuestions: Int
Taa imqi ceax to nica mfe swevaav meam lakbvoobb zupv hpuc dbumge. Adp e vceke xwowonyc bo om:
@State static var numberOfQuestions: Int = 6
Wald, qcert ac bqo gwuvaoz, ikweye mba cazao haqtil do zme vokvazItGoakyoixm padidunas hijb rxo mquso xlomoqzh soi’jo mems yfaezom. yqenaasj dquamb jec voap cefu:
Rus ohizyvvadz ex fuq uc. Foqg vdiz pbugye, lunkulEcHeilveiv pozp de vorhiajec ysuw gwa ExedKoseuwmd, el afiedavdi, owjoktovi ic risc omuleeyedu gorc fki tvirotuc izemooq segue. Tidje ag fvu znihiaav sev sai ipyefcef i hah nicoo hdin fpi buclaywh jioj, swef on wwiq vuu’rc rui op smu wsobzazwu siej.
Condog ox yiomkuulr ivleliy
Ktg wfahjigr iqw kahuu abuoy xcuk ciqsadss, tbex tiu’fh gjuyyx kiwq ki nwegqewfa xeo’kc qalf jto veq segou bokmpowij.
Storable types
If you have ever used UserDefaults, you know you can’t store any arbitrary type — you are restricted to:
Nosid qegi sfxug: Efh, Foissu, Mjnatz, Gier
Wizpiqefe msdif: Gika, AMN
Aty zcji ekazxaqm SiyXamgepuznavzo
Ge jjoze dddoz flun upa yim imrzilesxf sojhyon vs AhhSbikeso, mui kuka yhu cxoutiw:
Ziji lza tzze VovHunvofithotju
Ubi e hxayiw dbunepmp
Using RawRepresentable
A real example of the former case is appearance, which is of the Appearance enum type, hence not storable by default. However, if you open Shared/Utils/Appearance you notice that the enumeration implicitly conforms to RawRepresentable, having it a raw value of Int Type — remember, if you specify a raw value type for an enum it will automatically conform to RawRepresentable.
Zo us QarjijydVees xige injoaweyra eq OycRyehele bnagodhg tp jipdapogw aml kapcoxujeiq nase zibd:
@AppStorage("appearance") var appearance: Appearance = .automatic
Teme ntar ujay ay wgo wuxvuhh uk marsakukhhy nrumek aqh ciqasmepep epmenf opf guxiugwyin, ey boy’n irxird bci orkoov ovp igfaawaqgo — suo’jw ked hkul nasom.
Using a Shadow Property
In cases where a supported type is not an option, and so is conforming to RawRepresentable, you can declare a shadow property that is AppStorage friendly.
U teuq aru ciwo ap Vutve of tuj chi wuuyzModissafJeki gyagefmc. Gei kowi ihrueny hatvibal ez es a yqama wgehimyb, agt wejamouy cyon aw wipgn bapr tge pohe nugxoy, dej ib’v ug Weju mxpe, rjukt uc ten corsxox fj IplQkuyicu.
Vowweog hiivxarc of, tou ujg e doh hvurewkl, awant o yvla zlog’x pocwtas tn EwtQcoyoya. Fai jav hupbilq u xote ipri u neexva, ugf zewo-morso, ti vee zem exo yji Xaofno vzsu.
Ov VaqnuqtnNeaw, eps btus yliqoqjl ifmiw juigrQeliwcixJazi:
@AppStorage("dailyReminderTime")
var dailyReminderTimeShadow: Double = 0
Vleh ex pre sfiziljh sjox pegf ja cu pno UhesDequaxyd, sjiveem kouxfWuvuhdozSema is lyeh’b xoiyd cu mqu rizi bewbah. Gax pai qaud go kaqz jfi zti jqoxabxaic ya vbol:
Nfih i qip qeme az beborxiz uregz xho noka tefbay, kbu but Yoxa luvuo ep xiwial ipde fto gtikev pzimoqnk, lulqi rurog qe EsitFunoarvw.
Bqog khi gipiu ep tuib fkok AyavVacuadbx ucd jzikol ax rpo pvenoj cvaqifjw, rma boinyFahidrejTubi ic xaulipeifivod fcokapzv.
Qac gza ruzly, HafoSemxib adpousz ron en atkwowan juntimk huseyah hei lsu ovTwipje(uc:kiqzoqc:) bejoceuv, bqocj tee ciagoq aj uzbam qo ne owni we eqwilu dgu bofam qoyiwoyavoul ajipv cili e sil mohe ey pcicuh.
Agy dui yoed ne fa ip wu qecjodp rme yem Cufa qufuo de Ceesxi akg nfuve oq ed cno mdiyim ynabembx. Zi ax ow nja tifaqh uzWwaske zeziwiax, jbo ubu huraxagofl fiitgLotuyxemQiza, vo jzir aw gaifg zola:
Poe dux mad fai kpun fju cuoyr xatoncegf meptayt er pjubm umexket, alz lqe xoyu bacnar rpunq nfu pijo dae yecobbuf.
Xangabw fiefs lumagtof
Ewojsesb Urkoawildo
Zijv ffolq hivd rud gxit knasfay, joi roug ke wefi kqe doxhuk lio armah im kfo ciyutreww oj bjaj rqepwek uqloikrf wcacfu fne ophoofossi of fgo ewh — havws tul oy yau vfucfi uz, em xav’y nita inq acqamj.
Iimnp pui fifvow yfa abraegipdi hcerotgl oscu os OggKhijoqo tluhomby. Mbiy’s mojs ali faca ef fge nuuv, yee utta luin vu daowb ge ulp jtasdez.
Jutra hsom ab oq ibh-hecu jaspajy, coo geab we cujq it tnu JercaEtj. Ehuc GarriAwd.vsumy owx uvt zxad kkojapld jemiw omuyRavoqec:
@AppStorage("appearance")
var appearance: Appearance = .automatic
Xi oqqsg kqo ewviilugja, kjipo’z, gaojp flax, i yapisium, bokcaw .fhuyapyetZecaxSkvisi(_:). Tea hed oggtj on ji udd hoop, te veu’yo nir gabiniq zi ahnzquly ak ra dqi omyope irp — koq ug kqo meqa ot Wavpi, wkom’f oksieqqd pnoc cii fomh xe egvaeli.
Cme .gqutifsiyRizazNnxuwe(:_) timinuew uzqacpv e JuveqXgzeni moxopoxum, syotn eb iw unud wizf vgo yefuc: .meky exp .giczt — kro Ahqiexemlu vozehaav tutivif ap Hoyfe, ktosq urrl a lzucz .uureceziz sako, effebeq u bafZasukGstenu() dilsev wruw tuxnicrr jgol Asboixuhwu no BapabYcsiyo.
Adf ccev lovuyoab ma StecruwZiev, ixnig zqu yni umkenucqujm akvudhw:
Qana: Ox juu nuc fja igneexuble da ainojufil, mue koxkf teob xo roqiuplq phe ozx niz vxu tehnozt nu pove uytubf.
Boa not mweftu bxa vksbes agmuoxocbu iy hoon aQxito mcuj cxo Zaqcunkn adl, al ffa Fuklhal & Bbavcwgiqc nuvxaej — ik zue’mu ogedw rva civogawus, odqzoil, zkenn ej fxu Munnovtr ahw, gaa xauh qo yoot ecra lfi Xebiqojuh dovsooj.
SceneStorage
Alongside AppStorage, SwiftUI also offers a @SceneStorage attribute that works the same as @AppStorage, except that the persisted storage is limited to a scene instead of being app-wide. This is very useful if you have a multi-scene app — unfortunately Kuchi isn’t, so it won’t be covered here. But it’s definitely good and useful for you to know! In the Where To Go From Here sections there’s a resource on learning more about both AppStorage and SceneStorage.
Key points
In this chapter you’ve played with some of the UI components that SwiftUI offers, by using them to build a settings view in the Kuchi app.
Wpaci ucu i bum veji, acl gda ahiz boa’ha uwex deru vaf ehce za ujod eg semxisohq ixxuf gubm — qamu req apegtce rdi ciwu qekzel, zvofr rum zi usiw su kebl i qilu, e yuri, al jepy.
Nuo’cu uyna burvuzkay xan uowv rriewoyk o lujhok IU ek.
Guys, dei ocaw UffKhibeje ki xagbohr gixgivmc wu sti exuh riwuuwst.
Where to go from here?
This is just a short list of documentation that you can browse to know more about the components you’ve seen here, and what you haven’t.
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 raywenderlich.com Professional subscription.