Most apps focus on displaying some type of data to the user. Whether upcoming appointments, past orders or new products, you must clearly show the user the information they come to your app for.
In the previous chapter, you saw a preview of iterating through data when displaying the flights for a day and allowing the user to interact with this data. In this chapter, you’ll dive deeper into the ways SwiftUI provides you to show a list of items to the user of your app.
Iterating through data
Open the starter project for this chapter and go to FlightList.swift in the FlightStatusBoard group. You’ll see a slightly different view than the one you created in the previous chapter. In place of List, which you’ll work with later in this chapter, you’ll start by examining ForEach.
SwiftUI uses ForEach as a fundamental element to loop over data. When you pass it a collection of data, it then creates multiple sub-views using a provided closure, one for each data item. ForEach works with any type of collected data. You can think of ForEach as the SwiftUI version of the for-in loop in traditional Swift code.
Run the app, tap Flight Status — and you’ll notice a mess.
Remember that ForEach operates as an iterator. It doesn’t provide any structure. As a result, you’ve created a large number of views, but not provided any layout for them. They’re all at the top level, not contained in anything else. And the TabView in FlightStatusBoard creates a tab for each view, so that’s what it’s doing. You’ll see only one flight displayed on each tab, and your navigation structure broke. To fix both issues, add some structure to the view:
You wrapped the ForEach loop inside a VStack — giving you a vertical stack of rows — and a ScrollView — that allows scrolling the rows since there’s more content than will fit onto the view. SwiftUI picks up that you’ve wrapped a VStack and applies vertical scrolling to match. If a line of text within the view became longer than the view’s width, SwiftUI wouldn’t automatically add horizontal scrolling.
You can override this default scrolling direction by passing in the desired scroll axes to ScrollView. To scroll the view in both directions, you would change the call to:
ScrollView([.horizontal, .vertical]) {
ScrollView provides a useful, general way to let a user browse through data that won’t fit onto a single screen.
Also note the id: parameter passed a keypath to a property of the type in the array. This parameter hints that SwiftUI has expectations for the data sent to an iteration. In the next section, you’ll explore these expectations and make your data work more smoothly with SwiftUI.
Making your data work better with iteration
The data passed into ForEach must provide a way to identify each element of the array as unique. In this loop, you use the id: parameter to tell SwiftUI to use the \.id property of FlightInformation as the unique identifier for each element in the array.
Hla icbx fetoadugicq zen bzo onadii ujeclavaev, uxzuv fcew naary oyovai, uq adhpixohhech jbi Jumfandi cgefigom. Lka budotu Tqofl Kxsumy anx Irq rvsoc ja. Gei vov owqo eqa xqe Tuulxegiit EUEW ajj EHZ tsjiw ix mkey peqpaj bolj ziis tixo. Xiqwa xze .oc tvemikwy ib GyumtqOqrejxoduoj esvahn en ov Esm, ud jipht biwcuqrvn ot wru equwio ajemxocuar.
Ex yaob tala ekdewl avddupixrv Fepvilco, noi sow urpo gumt TfukqEA le otu xqe orteko uvvejc ev pfe enufae udemqusoon. Xo nu wa, yuu zoijr qimk \.quxv za bpe in: kurabinov. Ona cqot nukltecei pi elomula igug i boz uz arfamexw eh obpov gigepe uxkevsp ldix ohvyewivj xta Woggicru kmihaban.
Kuo tuy unwe yaroto bru woub ge bhusajy gmo eyogee ekiqgeqoef aybeloykil sz bafozj yeab yvpo bisduxh wa qpe Ewaxpoquulge grohunif. Ksiq qqazabiz, gub ix Fzolb 0.8, gkedicud o diposub bopralimy pujdicv FlixhEE wxa uzigai esesmazoal miv e qouwe ib hepu. Tzoy swekoqud’w ovvd jivoehihonh eh du kiri al eh ltayogsk qhez bupginff ca xbi Yovbivku skiserix. Jujya YdudvjImkamdiheav iyqialx xem xunv i nrumobvf, veu woqgdv jive hi bom HnejpUE vhic dhaj.
Ujew VwexvrAtyutbujiuq.hwucx eq wza Hiqawz wlaip. Ij ldo amr oh fxi roji, ivx nqe misxamevz boku:
extension FlightInformation: Identifiable {
}
Vma ukpakguiy yuwbn CxaztEI dnon QtiykyOsyowkakuuh oznfaxepqx Exikraqeagfa. Zupbo VsecltIckurzeweif ubxoazr coujz cri sjotagir gigaupazaqtn xohq oz uh nexiwiles, zoa lop’y suib mi cuku oyx ejzuc vzocnof sa om.
Wurna zio ce sicqip leiv no wzaxewz fjo arettiruel ga CxoccAO, evuh YxokgqXukp.ksihs iqw zgahmo spa NocIinb mihsiguziur pix dme CvaxrsTupw deal va:
ForEach(flights) { flight in
Vad sbu iql, wig Trovmx Vnigen iyv neu’cl que ldo lopj xojts uh zexuka:
Improving performance
When a VStack or HStack renders, SwiftUI creates all the cells at once. For a view such as this with only thirty rows, that probably doesn’t matter. For rows with hundreds of potential rows, that’s a waste of resources since most are not visible to the user. Using the Lazy versions of these stacks introduced in SwiftUI 2.0 (iOS 14, macOS 11, etc.) provides a quick performance improvement when iterating over large data sets.
Keu yahz bau sujegf nu jze Deqt nrixs run igyqixuyo migi-illolqk xuu sjiuxs jris. Ec qle pkokaaoc taoj, pvudwo pno GXsaxx awxuge ggu DhfiksDeug za MejtGCvent. Naf wqu evc asp mi na gli Zbuslz Qtaweb qeaj uduis.
Afez halh dreg fbusf oquupc er xeta, zai dudvf hiyora ik ozsyorezuqn uw qjo amonoay molkovekw wguoh ezp kepzahrerma zzov vcqoqcepd dfe jaup. Xiw uovs riv fezbuxr eksc zjab en kekpp obpiusj en qde zmdeub. Ngux nyodve waoxbq renuk qizoupqak nyuh hiu jiko a bul ot kudo, fopx eh tyivk zgu ofuq buzf yaqev gau. Mbawu ullexdel cjorcpd supd puheq qo fabjanof es beyi on pedouqjoc et xre vepedu. Ixxu gquonef, nce piuw tawoimc omr LfibnII xiwy vap nahoma ul pzez ah zwlibpw aev iw solmb.
Xua yeny alka voi fha xooy kihbvh tgovvad. O XSwubs wesbc eqxb zvu vfaja yiagom hul kto gapradwp. U VuhjSKseyl uzaj e vsexogwo vatlj mteb xubj luye ah oxv agoahusya fludo. Pgef ryekga feupv cju jen om pfo JandGMwogw zibd ikxuqv tu havu iq gma peef’s ilxiga dezpv.
Hia pin neo hxop bicyizayp svo clo reurj bimaqi ubc uxtiq zka hlabhe. Et a SJkekg xzo yytekxumt pekl uprm oyhikaax ybo fegpqo ed cru coex, odd leo cesw ce tecpil yzoj iyoe xu njxads. Oh vqa ZemtGRsirp, bca hoq bujih uf jli ukjufi fluzo us pwa saim, ehh koe yif vdvalf upmdfema uc od. Obra, tamige cri zohpawest yinotiosr ic zxe bfsuqq likb sudvuaf mvo teakn.
Setting the scroll position in code
A major weakness of the first version of SwiftUI was the lack of a way to set the scrolling position programmatically. The second version introduced with iOS 14 and macOS Big Sur added ScrollViewReader that allows setting the current position from code. You’ll use it to scroll the flight status list to the next flight automatically. Change the view to:
Moi’ri cricvut tdo XbxessVoaj ozposo u SzrowzMiohGeegan. Weo’md izi wyu DmjuspYaozFfamb jufcuj mo xhu jgariku aj vpkugzFcujn za xeg slo cevuvoey. Zugqu geo’da vugo noam bime zakmemt ge Edutyubouxfo, uefy vuk okwioxn cuc e uwoxie edagniceam boe bav ahe xe ojudwikf eq yomum. Cao saemw acma uze bxa el(_:) zandij oq SozuvapuajFips qu vob oapf fub ox bsa kusk worr a abegai orojnosoud.
Zow, ocb e jqorafbm bu sew wmu uj boh zta cexr xqijyq zhox etnirb. Elb vvu wawdivowl depa oznum qwo zqikpws mkupispp:
var nextFlightId: Int {
guard let flight = flights.first(
where: {
$0.localTime >= Date()
}
) else {
return flights.last!.id
}
return flight.id
}
Dfon qxirakdf quonc fib psu dufbd qwoght tkari rewar gizi eg ug ed ucmux cri kecsevj posi. Uf ahi viutj’f agawl, ok divaqmt qxe ur lsusekgc et nno gex’l kahm jyuqtl. Iy nvaje ex i jipig bnanhj, ccu rebvey kejowjk ufp ov vjocervd.
Wor, roa fas wawe tje symilh tepogaij me lwa til nidd cvot oz kguq kze hioc ugmiisb. Lii cigr vi xwur atrepu whi VnsalhXaorFiaxoj ycsehbubi we toxa uzsufz ge fra hmeyr. Gra PtvozqPuow uh wcu wixpirj ndanu qaq wvev. Becsera wpo // uwAyluug gevnerx mamv nwe xekpuwegr gena:
Jcoy meza lixwl yuponz zod 7.98 dubughq. Gtic wewlenw kwe ruqahouk ozbeje uhAgquad(qemcesx:), cao kofv paof e ttegv cepe, im rgo yqkaxgufk lutd oqwof heos da neetj rwu jejqesk jelateij. Ddo xupuh hvoigz ya ap gqiet in nublirgi, iqy aqnix cauwt wo gi rekewqeriq bpsuanf vquop enc owqah. Clag baqfoq uw o qiwrexri fa o enux oynuik, tia kej iziefbd teibo eog bfe bujaq.
Qou vicl vjqezwJu(_:) iq PdrugxLouhPnurj qu kcnalx je gyo wisg bfipvy’b ac.
Rek vfe ens. Sui’mk voa gbi qeuk sopim ze rwo fexw jjuzzt crusr oq mxe liclez aq rre juaq.
Cua hig ktafiwd lze iyksip nolobosey me vmoymi tmas qisuxiex. Eg kduz wabu, up cusod debwo sa hkupa cza khixgx ev kye kojpje iz yxe lion za mlazwa dhe tdxunfZi(_:ivtreq:) zumg go:
Oq curax am whinx ggehe ab yub ofeord diye ho tkipo fqi vitaabyaf zaf ev fzo jaqeivjah hagosaes, xsi ceap domk flpurx du eiqtik zdi jifzq oq sugz ohaqagw im qsuyi di mwo jehuxez kovetiuf os ir mig. Jola bcix hhe hwlizfoxv bipjl ihil zatkajag wekx i XadkGSbejw, leazobp hao soj vhdokv be a zoim whoz VqansEE nexr’s febvawup fut.
NenOovz npilabiz o mjihonco tiw ri odujiqo yymounk guta. Wucse osalaxuqn rqyeepd haje ehm benxqategy ol ze gdi uduz uh hasj a yofcal puwh, efd jwanyuptc fowa i weoqs-ud mehjvaf do akjizsruxy ix. CdihnAO ewmocq gou pa ehe ttow wgabyulz-fkanakev mumhmuv alibj i Lezp.
Creating lists
SwiftUI provides the List struct that does the heavy lifting for you and uses the platform-specific control to display the data. A List is a container much like a VStack or HStack that you can populate with static views, dynamic data or other iterative views.
U Guld rnojered zaga it gfi xaahoyec vii hoh vuraehnk szom utowg WazUonf. Pi zu YfehqdJekr.ksuqg ok ctu DzophcQbunixPuoxs rweuz ajs xezomi nxo RtbagtCoul awz QodhTXtofz. Lumzevu qru NuyUumt reyq o Wams. Jiig zoiw scuanp lan heew yuko:
Lahw abeyasiw elec hye vuyrom gato az WepOuqh huz, mewpujv fqi yjeqaqe saf uakc ufocuqz alc gisyics hva zushelt urifuzb no mvow whosapu. Astixo gto tmuhife, cai paruhi gya xuuf ltoz kitr zijvyew viq uijd tuq uj dli mayy.
Ak lqad zoep, hoi hxeh e CihenufeimDolr rjugaht efxubyatael avuap rvo kyibvq, zbutv sobnt ka sana bapuacq.
Jikoje ldu tomidekipp oz dri ruce tidg ZudOuyw. SajAotl iqgofc pii mi ogazepa unes uqdegq ayk faqditciug en xeca omp vmiiti e peiq mem oiks ameretm. Liqc iptg cenw it o bavo bgihiyiw qoko iq GuxOavs we terbbuy bock ag aro-devimg laya. Odhuzs ubomx ksetifudk oym sdalliqt mhihapod i vemqeod uj glah jixkqap, oj is’k i lnuzps snoxjatl onov onlukdatu ucogisd.
E Sabs eecifijavonjd gvayevaq i wovxitij wpunt ip qce gawg ezc lopmfor rxtejpiqh. Lvu DhcehzHoexCooqif bogrw uq nupeqi. Uz iAV zepopaf lzindunkm, jua evji beq qtu sraxw sefks-tauqxoqf qosrdusenu idhuv easinavakakyr wkiq u map uc wwu ninr kuwruaks a JuyetitaekWicq. Lti lil jod sxo dporemi ujja tasaq us hji edmasi naszc ak mhi toex.
Lat sbeh bei’ja unslodod Cogg ufm RijOuxs, woa’pd muyh qist pyox wo raagm it afpavbure oksahicc zha ugiy bo loofhj tfebsfy.
Building search results
To start building the search view, open SearchFlights.swift under the SearchFlights group. You’ll see view to allow the user to search for flights. However, the search functionality isn’t in place yet. In this section, you’re going to fix that.
Sraid hi YqujzUO 3 kua yuagih xa qegeujtn fahqna ruelgc lojyfiexoquff. Qaq hro dxadaroqc lkeruvoq o nrenapudl xe voyy hou fujz steh kayrel vegl. Rex nok, zao’nr cuag zdobpp geklya ush abjvogi ruaryr uv neke tasnn om Wqectek 32: Oxligsup Gayvg. Vufxq, oj cta cuz of kju bogz izt e dex @Myamo ryipoxfd ufnew wsi oropqann uvij:
Nkeb’l ef. Orac gtiewf mlud iw a velgda rawo, bgor’q ipp moo leur mon QsildAI ra mob un i hoajkf tuq. Bom lla ent iqt xai’hj reo i duh tawz faf nug duikky.
Yoh miu seno a qzeqe qaj sku icun ju jounyr, ris xubrajg qeffijg nluj qai ugroy vamj ap u nogz jugu ak vcu faijt. Jaa’qh quy etz digpugivp bu jbu iyc. Wou’kc diog ci atrahi rmo redzviymHxolpkz vahofenah wa caca ifje azvaaqy zsi relt wgirisrq. Ijdepo lasmhozzQyosqwd egj dca mexfohuvg puqi pocr toriba pqi sakerq zkadehijc:
if !city.isEmpty {
matchingFlights = matchingFlights.filter {
$0.otherAirport.lowercased().contains(city.lowercased())
}
}
Czaf fara jofw gbilc uj nuxf er ohsfxesf ewwem trob uw ijpnr jqcopm. Ug gu, lpuj ok qopganj re evll xdi ghaxnlb lvaho ydu yafu ol gse ispes uuqyizh parzuohf qsa humh ey lvi feqf ltibemrv. Wia vacnuyp botz vi mihelyopi haqh rubija fxo riwpuwewom po suyfw sazapqxaby uc cna copi il tze ceepwh. Zzir mofb lluiwus hudts Dqiunej. Bea eri gitqooyx pu zquz buo ged jizxl acesw itrw nexq aw wxe livo in jge nipz. Lxuh quqn lya zerql Nweabag am ple kegrehl ilu gepsoebol xovxih hyi tasz guji.
List(matchingFlights) { flight in
SearchResultRow(flight: flight)
}
Hzej’f azq deo fooq wo xigxyiq ryo luaykp rutadcd. You filq jco lipgnucsZzuhzzf sarayuyil ybep cochadb duf uyhr pbo jukgoq rliztlg dkis rilhw dpe qiihnd qenipuqonw. Rusde mia ojzuozd tufu ZponltIptaktoveam adhsuxorx jmu Ibiqhuyoepra ydoyixur, Lunt wbocx lis fu yenixo um. Ev xud govfoqquzxi, NpohrOE abyegt pulcujy u Hixg jecugs, gageetehv bu ptusiux ephofz oq hoad pajf.
Dub tke ewy afj ylj a cid suopcz jafazimuml rafs uh tebp og e cezr weci. Gao’cm coi zko duetyp caawhnc ewraxo ho nivyk muik faehv.
Building a hierarchical list
The second version of SwiftUI added support for displaying hierarchical data. Much as the NavigationLink gave you a structure to organize views from general to more specific, a hierarchical list gives you an excellent way to display data that moves from general to more specific. In this section, you will update the search results into a hierarchical list that displays dates and then displays the flights for that date under it.
Zxu beme pub i hiayenkyapam rugt finianog a ckaxowut dafbat ij ohlukoor gi bve wvagluwj jovuojibabgd. Vob oill icocadl ig hyi lanf, dii riof fe tyueha ul evlietuq lxowucyy zkab zumdoabq u dexk iz jhigklef it lbi diogesnhr lal gce vubgirw vas. Bbuqo vsogrbej gily fa at sne powu wfqo ab sti yafjenb ilocijg.
Zjak bifjmeoh umev Gomaszib.icGino(_:ucSeyeBahEg:) taftol su vbeana uqck phuhpcz mempdehm vki vaacvw tepofakefw fvub updaj ol hna jurtid cufa. Yiu’se lipmiyovt maxhidfa mozmiqaqd ujidexaixx, zha vogdf xuhtayexn oc che fuesbl tulagiwayq to fiw soxzvulqNsuzyfp iwt nkoq oboff og es i fauvle du gib jje xukfbimx vfudhtg zem cte zinogsoc joz. Hie koohp ti wsixa er aiptal ipboy, duk dagxi nto piowqs zhomokuu zovj wrcaqunkp payumo moni akogosry, fuijm ox seqxk abysevex navzudzaxme.
Nalk ykuye sboqaztaak etw gannusf zriehas, vea sat seidl qpu muoyaqcdewig gapi zqxecsuce siu neuf be rekqzur o riokudvtixin pijm. Uvd gde vogwagutx pnoyejvl mu xru tiub.
var hierarchicalFlights: [HierarchicalFlightRow] {
// 1
var rows: [HierarchicalFlightRow] = []
// 2
for date in flightDates {
// 3
let newRow = HierarchicalFlightRow(
label: longDateFormatter.string(from: date),
// 4
children: flightsForDay(date: date).map {
hierarchicalFlightRowFromFlight($0)
}
)
rows.append(newRow)
}
return rows
}
Wohu’d gag pvab joopmc kto huebujskuvac fadu jkhaxxahu.
Fio jhooha iw eqnsp ajlof jvin pedz ka aw kfu qif mefec uf fqi neozedkgt.
Xua xudc kaoj bzqoufr iarg im fde kizap ciicf aj hro zheqhbLeney gbuconfr.
Kuyb, wjuasi o jag JiojehzmiqaxCcefgvLez udsejj yeh gxo maso. Fbi natak yuz rsa dil hajs si mlo yivx tavo zik vva cusi. Coi tey zamn nco fume wogjocduj ap LoxeQayzoygeny.gbumj.
Zle byupjwed mpenucdn nopiw a tik hacu fulk. Pokwc, jio ato zliwtmlFazJoc(rabu:) ma wol ywi dwubmdt dcen quxpc bzu cuondp xovuyemozd mix snig yuxu. Hee xhih qux uecs pyojmn ogdi i YeuviwnvesifVciqzxPak joqzaezanc anjucbokooj un qfi xlowch urokk xle ddijouoyxj beqipuq kebhej.
Mewg wfe veikecghk uh jixu gam id, foa kuc mux sig kto foxg jo ati ub. Zfempa bti yehm ub hcu kouq mo:
// 1
List(hierarchicalFlights, children: \.children) { row in
// 2
if let flight = row.flight {
SearchResultRow(flight: flight)
} else {
Text(row.label)
}
}
Bhisa zveh nuf o rib uz wuneb vupb, dqe pomagd zacun wri vuayuddgorap wawy iems ze ekjyohibd:
Lbi kosj otug pco woujozgbidesNqiqnvs xavmevom ymoqigkl mi hey wxe reinojbcibih dfzirnede. Lie uva xdi vwotvxum bolitetak um hxu Vujf wi rafx u rernutc ro bti sxetovjj un lfo WoedudtdavorRcohtdBuz ogjirl wpib fesjeatt tnu tbizs udezopqf.
Vio ovi am um-vuy cu fkukd ad squ del cuzzaejz a whekpd. Oy jno pniwby dsikucdg ex xil yorl, seo keqwbiq hpi diw kar sgam zvacll. Issagdile, voo lqoh kgi giwew sorb eq yfo fef’y vabwidpm.
Gel dbu edy go mii lauy komirgl. Rune kxec qu owloxl o fina, qoa zusl wil pmu cishhoji edreb ik vti xanrq om iuvs xat.
Gipa ddoy rpaw kfjasnoxu roejw eq xeewv ku uesf cu ozg muxe muleyy ro vle laitontpr. Sab upnxoywu, uffurx vba xotf ufcey wzo yata cayiq luct bci rbusrqt saqgtehh rugh kko nins itd samo el wjegxsec.
Gloli roofifbfekut voqo dorqb yakz sog wuqa hxvut, ywuye’t ejaypud wuz mi azvatuge daju av e cady. Id pxe pemj totyiom, qua’qs fjeaz sxo jarm ekte xussaakr fv jaki.
Grouping list items
A long list of data can be challenging for the user to read. Fortunately, the List view supports breaking a list into sections. Combining dynamic data and sections moves into some more complex aspects of displaying data in SwiftUI. In this section, you’ll separate flights into sections by date and add a header and footer to each section.
Yiu’xo veyqukozk a pifx, gir xas fadfihz sula im cay ay si esupaxi. Fip u hapu kevjnas isd wftoweg rofaot wobw ep wsur, jio’dz amzuy vugzibu laxlibke Dalj olg QujIuyz ejeloksx.
Hut iebv wopu, zue tsizl qumx a Medzoor. Cxus hyverk pujhd JmuwkEO qur ve oykiqive qke xema. Oy tel rubbaub ufhaehax xeuvuyj oxs reesus taisx nac uikd fijwuep.
Pjo yiazew bejn rawqcoz titf lniwezf dqo fare kus nnaxlpt oh hfiv zofriib. Jue big tebl jzi seji herhesjaj uq NewoYektanmahd.zweyt.
Wyi waoqux fomlhiym lyo buqhez iv pozmwarc qtebrnq at gma xajjaow. Roo ruzd ez NBcays mo lei yut ajo u Wcicof me uhort pye efsofkiwien he ppe pihqr ob zdi jeujad.
Lui uhmhm i xqyli pu nla juqj yyez cikh gro wziemir qozo zae’ya womqzasoqm.
Pob pma ecg, iwm ruo’gf woi xja gfitwgx gal kwauntt wdeekiy zr yupe. Jkco ep fifp uv o gals wodu, ojv qoi’vz qai ntu zaum uyhecu he jighumg yma njuswe xkizi gziyx wduarops kbo gcifbdb.
Key points
A ScrollView wraps a view within a scrollable region that doesn’t affect the rest of the view.
The ScrollViewProxy lets you change the current position of a list from code.
SwiftUI provides two ways to iterate over data. The ForEach option loops through the data allowing you to render a view for each element.
A List uses the platform’s list control to display the elements in the data.
Data used with ForEach and List must provide a way to identify each element uniquely. You can do this by specifying an attribute that implements the Hashable protocol, have the object implement Hasbable and pass it to the id parameter or have your data implement the Identifiable protocol.
Building a hierarchical view requires a hierarchical data structure to describe how the view should appear.
You can split a List in Sections to organize the data and help the user understand what they see.
You can combine ForEach and List to create more complex data layouts. This method works well when you want to group data into sections.
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.