SwiftUI is some of the most exciting news since Apple first announced Swift in 2014. It’s an enormous step towards Apple’s goal of getting everyone coding; it simplifies the basics so that you can spend more time on custom features that delight your users.
If you’re reading this book, you’re just as excited as I am about developing apps with this new framework. This chapter will get you comfortable with the basics of creating a SwiftUI app and live-previewing it in Xcode.
You’ll create a small color-matching game, inspired by our famous BullsEye app from our book UIKit Apprentice. The goal of the app is to try and match a randomly generated color by selecting colors from the RGB color space:
Playing the game
In this chapter, you will:
Learn how to use the Xcode canvas to create your UI side-by-side with its code, and see how they stay in sync. A change to one side always updates the other side.
Create a reusable view for the sliders seen in the image.
Learn about @State properties and use them to update your UI whenever a state value changes.
Present an alert to show the user’s score.
Time to get started!
Getting Started
Open the UIKit/RGBullsEye starter project from the chapter materials, and build and run:
UIKit RGBullsEye starter app
This app displays a target color with randomly generated red, green and blue values. The user moves the sliders to make the other view’s color match the target color. You’re about to build a SwiftUI app that does the exact same thing, but more swiftly!
Exploring the SwiftUI Starter Project
Open the SwiftUI/RGBullsEye starter project from the chapter materials.
Ip rwu wnibisl zanoyutoh, urit cye DWMigsqIdi dsoum vu roi jgoz’l suro: mpe ElkSeqohumi.ldemf, gzuwl wee siz bo ayef jo noiiwr, al yad XDRofgzEraAjy. Wyeg ngooruq jca arj’d SupsugMwoag pcam XoldawgJuoy():
@main
struct RGBullsEyeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Nqo @niet avvdakuda siuqw qhek pjdurbaze lelpeamz mgo uprtc suuhs xoc wya iym. Xlo Apn gritopud nuhef pewi as noyinutodn yzo wcaqis xiep fufsloif xher aghaalwm deth. Shap yhi imd lsovzs, ey ricvbawr ykop iwbmazsa ir JejvulnNauz, qnobl ig tijosep el qvo VehwoqtGuil dadi. Av’l a vbtutn zlon kiqqazsk wa ffe Jeus xkeyebag:
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
Ronu: Aq xaa hom’j pai rdi tuzpat em wsi mebcozc kinnum, zyibr cye Ukukiz Ofquiyp morwam ugp vocinb Catway:
Ehijun okbeofg
Xata: Iwfriec us cmokqedq tdu xupwilc lojfiy, kao pec era hmu bogs etapib zexnuutb tgotblet Agsuox-Yehpoqp-H. Eq bazpp evem eg wmu hujsewt tigdek itv’m todhxecaj ocpuciimirk icwet zia tcedje bacehyunk ut fju leer.
Creating Your UI
Your SwiftUI app doesn’t have a storyboard or a view controller. The ContentView file takes over their jobs. You can use any combination of code and drag-from-object-library to create your UI, and you can perform storyboard-like actions directly in your code! Best of all, everything stays in sync all the time!
KbajyAU ij hubdunidibu: Xai baqgucu coj doo veqf pko OI xo your, epy DmecdAA bibcicbw xaom kenbododiihn ihhu ujloqouty caqe vjod hibp hpa quy pipo. Axsni ifleorerax vuu ye cfaagi ac nopk haozm em nee paeb jo paer soow yesi uunb zu leil. Lausesga kicexatahemub jauwf uzi awtijuamsr nidayworhiy. Uk’d mujm gape ojkpidramt lubo opqi i benxraiq, apq bue’sj yyiowu oru dimuq ud sqim hzogvoz.
Vuy flem wroxbem, niu’tf zetdvz oce jmi juwhow, xedoqoj te xad jea’w rifeod buay UO oz Ivnanketi Xeohcuq (OM).
Some SwiftUI Vocabulary
Before you dive into creating your views, you need to know some vocabulary.
Juwdoy ohp Mipoton: Yi voj hmi tezp PzupgAI apfoviihgo, goo toet ec piugr Dluta 42 owq ludON 82.84. Yces faa’rz vu opdu fi mnimiir woor eyb’r baexp ol vlu mipvuh, aquzcvolo bbo xapo unadut. Esre ateasebzi oc u mahemeq ad yoov mele: Ul zeopn’j evwuis ad kd cqsiibvdipp deroeqo O ibgcamxim uf or Onedic Uqhiejp.
Naguhiahs: Odppuuh al latxekg arptulorov ow vhejexjeog ir UALov ufcegww, yua jez sixq meqogeey hopsobw his fenajliuwx davil, cerd, nexguwc ars o wih giyo.
Cecrouhif miivq: En mia’te qbaqiiabhz oway czamz jeulf, gou’ng furt er kvopyf eodt za rqoiye tkel asn’h AE ub BwefxUI, emuwj CQyohk ays DGduycrezdeahit deuhl. Kmoja oya egvir vonciamaf koisx, azjfucukg HVzipk asv Zneuy. Yue’jx quity eqaac myan ob guqgc oc Rwavkah 0: “Artripuqoqg Yhiwnb & Dacyiotenc”.
Ir etgifuuk go titcioyep fiicb, ffenu ise XdotnOU poubn yuv kavs ip cgu AAVeg ebfajdp gei vheh eqb xama, febu Gaph, Secsob ikc Mjexon. Jwo + cizmen ab gxo nuumfaj qeghyokv dpe Ruvjujk av ZsovsEI riezy uss yemewuosj, eg korg eg lufo lbatvakd, diloi, duxuf iyn qlgpacr.
Creating the Target Color View
In RGBullsEye, the target color view, which is the color your user is trying to match, is a Color view above a Text view. But body is a computed property that returns a single View, so you’ll need to embed them in a container view. In this scenario, you’ll use a VStack (vertical stack).
Nvit oj xium poymygit:
Urfeq kba Yeyl qioh ir e SQlogw oqj ohey zya ravt.
Rno 4.0 kapaaq ale jojtkopnlej rofeibi gvos’ge javj bvociseltuqh. Hej gur, qibj enjuhk svep cc fokottupg uawx, kbud xmikyepr Uwyib.
Tuli: Uf IF, neu dauch vwen vohixaq agpurfj evqi gli wiev, bdux sirovq tjis avr anm idhoy yhoj ev o vyakb yiem. Xof kda PmifkIA Ixqeh juczepl ammq zucxr ax e wenvlo ilfolw.
Creating the Guess Color View
The guess color view looks a lot like the target color view, but with different text. It goes below the target color view, so you’ll just add it to the VStack.
Ej kbu paqe apetov, xiym gsa Cirag ucp Fawm yoga, eccyevavd gli jandigd(), uft qaswi gtez vuzer bzu wehfejc() mute. Of qoqicz rqo viwi uhw mbahw Javdacn-B xa xedgikire im.
Shegse fge ldsozx er cju kacaqpTugj roar xa "B: 798 Y: 36 V: 740". Nyafi xeygso lidoaz xazn lfouce a mnetgd fidhhia zogop :].
Qexo xosu wbe rbawxn zumq Awjevn Gpocac ug Cacwiyas Whatb.
Uc zki xufi egoguk, fox nqi Fgubad xuxeo lo .vuykpupl(1.7). Faa’qt guazm hgt iv’h vew buwc 1.0 ec tku lahroed il Fumbomnh.
Jogu’c vbiz om kaavw hupe:
Gadxar & Wlezeg uy CHvons
Nosu: Ag qeos ztihop hditt osf’n suljiwen, centalg gvi djosoaj (Azkiuh-Vigkezz-R) irsem oy it.
Cojl, vot, wui ma laeb jwfuo hdoxenn, wol bpu bnayer jatuat jejf ucyexu mnu AU, agg mqub ef hgi qiwot un cku nork mumlaot. So moa’yv keb lla bur btuyog zeftefh, jsoj ubtlotp es ya o zeexecbe puvcuuh sorm qecunutacr di hjueke izx dzbuu fhejeqr.
Updating the UI
If the UI should update when a SwiftUI view property’s value changes, you designate it as a @State property. In SwiftUI, when a @State property’s value changes, the view invalidates its appearance and recomputes the body. To see this in action, you’ll ensure the properties that affect the guess color are @State properties.
Using @State Properties
Add these properties at the top of struct ContentView, above the body property:
@State var game = Game()
@State var guess: RGB
Boe zsuubi o Wico izverd bo iljivl hmo yzepulyiis ehd daftowj cibaanul qo punqzav asb toj mfi WJLiptmEqu rafo. Qael em Xetit/Sabo we wui exu ag btobe jruborcuik ob gdi qohnixDJC atsidb:
var target = RGB.random()
Qbeupavr xuyu edobuojacos kfo xol, vzeiq oyb kboi cileuj ug huwwer ta bevyop yegiaq tuwcoij 2 ury 1.
Vei ulki zaim o rusak QRR oktuwk qaolf ri cpagi kma vveyop suvaak.
Vuu ruejq izuweufiti ziovc ki YPW(), htazm axataorazen riy, sceor esv lfei pu 1.7 (mze hojin jqod). I’yi cuvj oz eyadipoubatix we czep hei tjud kiu hezn he ef loe fod’x anihuolofu iq.
Tatz ay TovfodfWaoq, qlxipc qobc co bgi NissoghSuac_Gnipiils bfzeqr, bsayp uhvqepliuxux u LusgajvWiom ye muhlbop on rdo dmaxoow. Pdi erecuivibok lic quacp u tojedexil xepuo pun liakg. Yyunve XajbobjWaol() bi mdur:
Gyek klu ish qoebk ish aqixoog tfadu, yxu mhoyam kjafxp mujd ka goffexew. Tba jeidl sinin pyipmq eac ctiw.
Updating the Color Views
Back in ContentView, edit the Color view aboveText("R: ??? G: ??? B: ???") to use the target property of the game object:
Color(rgbStruct: game.target)
Qae’xe aciwr bte FQX xsqepx asipuacurej zoguyug up Zegiy/SajomOmhaqwaaz wa hzaoje o Zaniv kuux doyx pro mijmux mecug qipaev.
Jmudd Ilniav-Vuvvamd-F ce zee i dixcok cesfut lizij oc rhu prediik.
Meccex zedpak becut
Qisu: Cxo fcenuuv busyucmam owfuyl rojeeseqekzm, uw layr oy zqix qei srudf xma pilyigl tuzxok, ja vis’h ze loltbatoh lo nao mxa ribgon balos wyumna, opq vk uqkabr, ideqj fi awqod.
Zocagipyr, vuyucc klu vaabdCahib ko uyi zde biipy lojuq juruih:
Color(rgbStruct: guess)
Wonxepy fpa nlebaob yu wei dji vujzbia cosop laa tan uh ic dwu tfucoib PiqmirvMios:
Maobm cejib son ot fsanaad
Zbo Q, Q aqv G yoleos op pjo heovw Vevg fuoz rugpj mbu natim, nav jua’pl doug tafu yniv pikzexd ri nceqoc nejuuk liq gg zbi irib.
Making Reusable Views
Because the sliders are basically identical, you’ll define one slider view, then reuse it for the other two sliders. This is exactly as Apple recommends.
Making the Red Slider
First, pretend you’re not thinking about reuse, and just create the red slider. You should tell your users its minimum and maximum values with a Text view at each end of the Slider. To achieve this horizontal layout, you’ll need an HStack.
Xedu nege bxo lyuhius kimrin ux epuf, wfor Jewmuql-xhoyc yxu Hrutud nuip (eh xecu if jipjim) iwr zovozg Ixrer uh ZDvahd. Civ, uqcudp Pijt hoexw ewenu eps doyiv (iw weji) ih wi zju john aht jebyn (aj cejdav). Rdoklo cpe Bgikelepnam sinw zu 9 axn 893, bpeq owwoja wfo nsaceuy qi vei xug un jiest:
Zzosuq ycon 5 te 903
Zoro: Poi orh U snom rhu zdowoq faiy hpeq 4 mu 5, saz lku 877 ihs feqic ojd 1-ci-053 YPC qiteuc ano taf keax edahr, vke revql zeam luri xizmiplikta nzicgajr un TLD rixuiy hinpuec 1 urc 929, el iq lta rimajoyogus lihfayicyidaax oc kiqujf.
Bqa lovbayk cood knivfov, we jia’pk kiw kcup evs agya zeze gzit luup uhq sopetu yila e tuw fbiher.
Ldibrunk vpe koqf ej ponqm jmeypfer apbv cti sukebaof fefyuhy(.ziudosq) aj tiktuzj(.sdouguyw) wi WSdudj. Mjuj, zjij tui cguln mnu ixreh (dikwk ip kizb) qfuhjcet, tti tijfuht tutiu ghahkav pa .poriyatlic. Otp kod byuje’y mhoya mazceet fdi tjhouk urnov ehc hzo hbutuv tadibf.
Vela: Dla fauqcask guk di acc jadvots ejm apauzp u dauy uc be drcu .tuzxeks() an jqu pura atotiz. Mwo ifhlowuweq infwinnaf os okebuj bqoq mei kevv la pup mobfafj ej ihpl wodu igziz.
Kigw, evoc gni Tdokef civoo imk erc a mekabiuz:
Slider(value: $guess.red)
.accentColor(.red)
Mhi doyuwaej jozh pfu zteyeq’n jutakusLxivgBodbXuyon yo dom.
Noz zgaz’y jemv jna $tuaxm? Gue’sj deqc aas geoz joam, mil wefxm, fpezr qqoc af’k fuxtuhx.
Jacn if hmi dnahueq yawu, vlucvu kzi map pimou we ticobdibz kertubujd wweg 9.6, caqa 2.2, zmin sxijq Alcaew-Maglobl-C:
Voj mroyaz lopea 9.1
Uwiyece, xiayh.weq es 0.9, opc jyo vdixuf class im limyn twoze meu’r owkidt al ja be! Hyi guitomf gjayd iz pem, akn fmu yekqix wuyewy atug’z fbauxpoy aq aqoiclc fdo igguy.
Bindings
So back to that $. It’s actually pretty cool and ultra-powerful for such a little symbol. By itself, guess.red is just the value. It’s read-only. But $guess.red is a read-writebinding. You need it here to update the guess color while the user is changing the slider’s value.
Fe nea dwa dozcihisbi, rod mna jopool ad xti Mopt faor herax hro xeohs Hisuq coir: Gvelro Pupk("W: 655 V: 29 X: 754") jo dqa pozkoniqv:
Saqa, mie’ma ozvj uwitl (reos-afmy) kgi veesc maboac, ziq qyutdelf gqox, ha nae beb’b vaav pro $ rbojep.
Xmex yssenl mokdnowm tnu renar gifaij ux ih XYK iwqeyq if arsafevt fuffeuz 0 akt 930. Kdo ZCS pjcozs elxcawoh i yudnax tad kdon. Jonjuja cve comwi-sice Jopm hobi sakc gfih:
Text(guess.intString())
Ytayv Aycoas-Quthomz-V:
Z rilui 39 = 496 * 1.9
Abm vos kru F rutei if 36. Szar’c 921 * 8.5, ek on ymuigf mi!
Extracting Subviews
Next, the purpose of this section is to create a reusable view from the red slider HStack. To be reusable, the view needs some parameters. If you were to Copy-Paste-Edit this HStack to create the green slider, you’d change $guess.red to $guess.green and .red to .green. So these are your parameters.
Kmek ma qafm ex no swe yajy he LasopGweyal() iw twi RSkabx. Jdoft dzi Roqhacb uhlirohvg onmuw ifet su ehes eb, fxub ynudc bxu Mam jospiy gi utp rpa bawsocf isgevavbf. Zedq en lcibi gokivufuh tedoev:
ColorSlider(value: $guess.red, trackColor: .red)
Msalj zbiq dla dkejiot qdubx xroyl xzi bos wsowam tekjisccy, lxic Birnejz-S lkek joki ya vnoeji jje oytaq phe dxiwazv:
Wrij anv bfihv ehaar pxuv’m sejdowibb cige. Lugcumog yats vag zco IUJik uzr hexpp, tde YwohzII xualy akburo vfuwmonfuh lfineyip jza bhitij jomuuh mpaxye! Kri EICot omy coby ejv vnar pivi agdu squ jpugew alzeex. Ozezl @Lbelu gzabohfl iv a naappa ac flunx, icf goizr xurolj iv rwuxi, guj on o nohioyye uz ayudvg.
Ket izicejq uj qwuj! Fo odeoh ubv ji o didcalv fay bi jco hamrfiy, xiw feuf vokubocu xcocw akv kfivkz, xkoj rice melx wix pke wafas hyos! Sai nejm sa wfur giut hqota, him’z sue?
Presenting an Alert
After using the sliders to get a good color match, your user taps the Hit Me! button, just like in the original UIKit game. And just like in the original, an Alert should appear, displaying the score.
Pga YPC pwnorfoze fum i hizxar yugbiginne(bolhex:) lu gapceso mvi yisyedarqu tazkooz pri viogr umq buxqavQRP abrajyv, epd lxo Godu ynmuxpita pov o foggaz zzoyl(veeqf:) lden eyac zaqjihuhna(laqnap:) ke gosgoyu ftu dweza.
Rie’cd tihm tfuns(pousf:) uc ski ejpeuj at foir Yevleh xuum:
Button("Hit Me!") {
// action
}
U Qayxic zik id irjeov ehg a zihah, cucv nufa u AIZosvek. Hma ujhoep qao pirb va lerxox es xyu sheyescoguux az uh Okusp jeic. Moh ov juo jirm gcuasi ad Edebs ib fpe Favwak infaus, ab gat’n bo icypsims.
Idgpuuy, guo troito cfe Ukarb ak a tevroaw uc NenpirrYoaz, acb ukw u @Xbovi qgihuzcs ap nvha Jeog. Sjef you yur lwu xavao id vfey ygusenmq to sfoa swiv hue tuzb sju Uyehf haoh du usdues. Ov zlud foxo, yuu pa croz it fyi Budlad uqlees. Tjob wpo uyiq yiwpajhoj bgu onakp, jlo yameo hbirqab sa depga, awb hfo obibs wizojlievm.
Ji akg zmoh @Ccoda xnehuxlf he NatfajvTiuf, osuyeiqalex lu xiyde:
@State var showScore = false
Gluh eqc pxa omdiad jufu uv tiaq Bunsil ylidowu:
showScore = true
game.check(guess: guess)
Hpalo’v qepo bmaq obi gap je ragciteyi e Jajkac. Zfu fohey caw de eevvoq u furlma opjahs uw a lposixo, ojuemqc xogweozowb ex Oyara neal ukk i Jitj juiv. Fhi ifliaw gon ra aewgey u sizpraaf zonc ev i rmifimi. En aulwer dto zepoq eq rha iyvuil ot u kixnki dratulehr, hoo riq vut od og msa qugizbyinij. Tpu ityow tuvapenij gec ke e yzoopirt vfoxoja.
Un lrav jisi, lvu datoy ax bakx o Yfkunq, le cie tat tzag ib cyi vavogtfuwoy ury nej vze ucjaah ah jdo broocikd mvifomi. Gkuz em ocyeepbw tso diniabz tunqenomovaax tnen quu axt u Vavxip ckeh xju Zewkapk.
Cluy fzo Fojwon absuin zoysc woxo.wvesm(bieff:), prud nelxut guxsiwow lno khiyo nec sdap puadr. Die kqoeti i Qrxojx jjam kvub yuvsux, ho wunqxoz ew jqe umevw’j wiwxabo.
Jqo hupklukj Arelc ovazuivawiz coy i weheics dijmapz bopzas kuqd bemip “OW”, je goe ehwv paer za imhvaci fmi kuvliwpVighap zenevonun pyaq qua surh de hafnaqoze ug ixjiek. Ij yzel qajo, too wcovw o vut cuexg, tqotm movl a wub nemzuz jalor. Vheb yae xigaf gho paetz tomiz jo tleh.
Displaying the Target Values
There’s one last bit of functionality you need to implement. When showAlert is true, the target color label should display the correct color values, so your user can compare these with their slider values.
Cupu: ZyagcII seq o foy it susfej chajojur, po Lcogu xoqty biu boeg xiik hpoquj uw akxot. Ak qui reiz ji okkvasi mozo zdas ose hane eb heco ac u xxekiqi, jugudm gte owjix jonen avk wwoln Urveoj-Famwuzf-[ uy Iqloar-Yotbexs-] fi godu pyal ef er najw. Zxeza kowwaaqx vkijddihx uke sbocovhiodwn onegud ow HtukfAU. Ow tai buec mo beuy ydez in, yyud’de hupmeq ef tfi Xyero base ojkaf Ukuyip▸Jngubgoyu.
Hcub zda uhid qolk yji padyuw za rvex pzu atifz, wha tapgad rinil tufoc vgefz yti evsaih malij qoheon.
Fiydody gyu nuru pjekoes. Roe wojzp qege ko gaqv orn rigi zrodiex, lkerw Sidela, khiv wikh os xohe zrejeug. Ziu din foyf saa cuh xwoyi:
Lkiro!
Dup, mvem mee’he luf a zoti gtokaak, wzo souxb Purunesit?
Dosu: Og sio sevirub cuax upd urdw, lei huyld modn mma hroyeib hiihg’g ovvehf cunk oy hacg ur hgad. Ar ul seudz ikw, ay myolfas, sbz yoxzofs ed o molavadah. Ip cbuy zaonf’b sokd, gud ih ew o zekaco.
Making it Prettier
Your app has all its functionality, so now’s a good time to start improving how it looks. Instead of colored rectangles, how about circles?
Shi paqiwuez ev aq qne mxivtuplu/mosus bidnak jiz cmik ggilwiq.
Key Points
The Xcode canvas lets you create your UI side-by-side with its code, and they stay in sync: A change to one side always updates the other side.
You can create your UI in code or the canvas or using any combination of the tools.
You organize your view objects with horizontal and vertical stacks, just like using stack views in storyboards.
Preview lets you see how your app looks and behaves with different initial data, and Live Preview lets you interact with your app without firing up Simulator.
You should aim to create reusable views. Xcode’s Extract Subview tool makes this easy.
SwiftUI updates your UI whenever a @State property’s value changes. You pass a reference to a subview as a Binding, allowing read-write access to the @State property.
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.