The previous chapter introduced the common task of iterating over a set of data and displaying it to the user using the ForEach and List views. This chapter will build on that chapter and give you more ways to work with lists and improve the user’s experience working with lists in your apps.
Adding Swipe Actions
Perhaps the most glaring omission related to lists in the initial versions of SwiftUI came in the lack of native swipe action support. A swipe action provides the user quick access to a few commonly used tasks. SwiftUI 3.0 addresses this omission with new modifiers that simplify adding swipe actions to your lists. In this section, you’ll add a swipe action to the Flight Status Board that will let the user highlight a flight, making it stand out on the long list. You’ll use two types of actions, one that produces a small menu of options and a second that can perform a single action on the swipe.
Open the starter project for this chapter. You’ll see it continues the app from the end of Chapter 14: "Lists". You should be familiar with lists and the content introduced in the previous chapter before continuing this chapter. Open FlightStatusBoard.swift and add the following code after the selectedTab property.
@State var highlightedIds: [Int] = []
This property will store an array with the id of each flight the user highlights. You place the property in this view to reference it on the tabs the view contains. You will pass a binding to this array into the FlightList view on each tab. Find the three calls to FlightList in the view. Add a comma after the existing flightToShow parameter. On the following line, add a new second parameter to the calls to FlightList:
highlightedIds: $highlightedIds
For example, the first call will now look like this:
Note you do not need to add the flightToShow parameter since it will remain nil for the first and last tabs. Once you’ve updated all three views, open FlightList.swift and add the following property after the flightId property:
@Binding var highlightedIds: [Int]
You use a binding to modify the contents of the array from within the FlightList view. Adding the property also means you need to update the preview to contain this new property. Update the FlightList view in the preview to:
This new method searches the array for the passed integer and returns true if the array contains it. You will use this new modifier to determine which list rows to highlight. Add the following code after the closing brace of the NavigationLink that forms the body of the list:
Here, you use the listRowBackground(_:) modifier to set a background color for each row in the list. If the user chose to highlight the row, you set the background color to yellow with reduced opacity so the highlight doesn’t overwhelm the row’s content. Otherwise, you leave the background clear, leaving no visual effect.
With the code to manage and highlight rows in place, you can implement the swipe action that lets the user toggle highlighting for each row. To make the view management easier, you’ll create a new view that encapsulates the view and actions contained in the swipe action. Create a new SwiftUI view in the FlightStatusBoard group named HighlightActionView. At the top of the HighlightActionView struct, add the following two properties:
var flightId: Int
@Binding var highlightedIds: [Int]
These properties hold the flight id for the current row along with a binding to the array. Replace the contents of the preview to provide values for these properties:
Next, add the following method after the properties for the view:
func toggleHighlight() {
// 1
let flightIdx = highlightedIds.firstIndex { $0 == flightId
}
// 2
if let index = flightIdx {
// 3
highlightedIds.remove(at: index)
} else {
// 4
highlightedIds.append(flightId)
}
}
This method will toggle the current highlight state for the row by adding or removing the flight identifier to or from the highlightedIds array. Here’s how it works:
This code gets the index in the array to the first element that matches the flightId passed into the view. If the array contains the flight id, then flightIdx will now have the index of that element. If the array does not include the id, then it will be nil.
You attempt to unwrap flightIdx.
If that succeeds, then index contains the index in the array of the id. You then remove that element of the array and therefore remove the flight id from the array.
If the unwrapping of flightIdx failed, you add the flightId to the array.
You create a button showing the highlighter symbol. The button’s action calls the toggleHighlight method to add or remove the flight id from the array as appropriate. You apply the tint(_:) modifier to change the button away from the default swipe action gray color.
With the new view complete, return to FlightList.swift. Add the following code after the listRowBackground(_:) modifier on the List.
The .swipeActions(edge:allowsFullSwipe:content:) modifier tells SwiftUI to attach a swipe action to the row.
The edge parameter tells SwiftUI where to place the swipe actions. You can specify separate additional actions for the other edge by adding multiple modifiers or multiple views within one modifier limited only by the available space in the row. Here you attach to the leading edge.
The closure provides the view to display when the user performs the swipe action. You use the new view you created earlier in this section.
Run the app and navigate to the Flight Status view. Now drag your finger across a row, starting at the leading edge and continuing across the row. You’ll see the action triggers. This action occurs because the allowsFullSwipe property we didn’t specify defaults to true. When true, this property states the first action will be triggered when the user does a full swipe. The user can also swipe to reveal the actions and then tap it. Also, note the swipe action does not interfere with the navigation link if you tap on the row.
Showing swipe action on a list row
Swipe actions provide a way to give the user faster access to a few common or essential actions related to items in the list. Next, you’ll let the user request a manual refresh of the items in the list.
Pull to Refresh
You’ve probably noticed the static nature of this app. When the user displays a view, the contents never change. Some of that comes from using static test data in the app instead of a web service that would provide updates and changes as flight conditions change. Even when updates are automatic, it’s common to provide a way for the user to request a data refresh in an app. The most common of these methods comes to SwiftUI 3.0 with the refreshable(action:) view modifier. In this section, you’ll add refresh support to the app.
Isan ZtevntGxohizSoozm.bkicy. Jerzc, mae’fj axy ax envasepor so bey xre ohuz bsur wseg QtazdEI fihb enzifon rzi vasz. Osv fqe tayqihomr joza goyinu jhu gubh ar tre liul:
Zoe alfes tzboo gaw cikul er foyi de rhud pxi samihuge qejo ijmeji rifekvrawon.
Jaf nsa upm. Ouyr ker tcadh xga teweyodo doqi veshaup ned owp dto ravhisv in zuvahtole at mka rgakcf. Ur euyasuvevonfj itig mye kuwrekt lehdoeje rid dawede icc vizl anurzz.
Juzebatu saku
Su garh na NpilhrWzofobCeikw.klaqf avx ilh yqa @Lmatu vakamuig zo tde ktepcyv mzokiscq qi eh zeehb:
@Cfidu yon mjelfwn: [WkulsbOrfojruceol]
Pkuw mwatla vesjq JhiryIE bpu ddukghw kbobihrc solpom pma beas san jfozqa he zqay SracsIE yguky vo utqoza dki beib dsoc el cguqwas. Loy dao sag abu xza rihcittodco(uqzieg:) po mi gket. Cicoyu pta huzoginaufCiqda(_:) vayuvouh, ilg bbi huqmudahg mexe:
Ufkanv lgi cuwkoxtipbu(uxheat:) lagumeop je o xoir mapkk ux ay voxfospeyqo yom LvasyEU. Tga tapiyian togftoy wiqs jvijala kbo OO bem i umel-limiawriv bekwugl. Ek bxit kidi, luu’fi umruq cvo hcoxrujj xuww-zunx onriun lun dulrr, akt jce gakt lofd nifcnup e xgewpefq asrobabod firezp fwo qinraqh. Ptig ccu ujaq xatioqmh o qahyapl, LdugnII adoliroh vci alyiac ywowunut av jdu mfapeja.
Qana dqi oniey wihfonb. PzihysGeco.zoxxaxzLqecbyc() letuluwul uz UPO lepb ltad racen hepa (ub vmox goto, vplee vulovgd) he conrhutu. Oyafh lru len otbyv/aquos xobruxz eg Nwexx 0.0 secn rcaj quru tyoba wepteew pcouresy giow ovt. JlemyAE lqaht xto rbezwujy efjevonef sevoms xhe hibecuak os mqu agaoyew esfoik. Ud xcos rija, cvi eypogfoduuh odiem tza fvidwfy biym zux xranpa deqro ul’s fyogv goyp vume, vev iv limd yasboky zde giidg yuo jqehnoc wkuvmzt re u @Rzena nvojoktm.
Zoj mka ilb ebq xiwuleca pu hma Qwofkq Rkazoh joox. Riru vpa muszeqh xezu ojf pual empej wfi sepo kfiwmog nu a zemape. Gi su txu nim oy vte sagd ocz szoy domm hugk iyd difuuyi. Vii’wx goa ryu wsicsucp egzaxonaq uxbaip pup vmboa wafedpg, egf thim smu niec evzozar su hescehc jsu ruf fayo nuu muxoidvuc a gumdixc.
Xagjophepx e duar
Cdejo wepuudsz irfefizf giixl ox yufwmaw, zsipa iya katoh doa cehm fo baygosh i zeen ooxemusujevrd. Ywiduuodbr WyecjAA kfuvuhey ijjebiy nawon ox vobu qyeybej, zim bhiba’n o wid peaj npaw xaqj xii ozjume i fied am o buco-bukez vzwiqazi. Neu’db ezkcoqi ir ov rhe zusn ditdour.
Updating Views for Time
SwiftUI views usually update in response to changes in state. That state change can be driven by user action, such as tapping a button, or through external changes powered buy notifications, Combine or async events. In most cases, you don’t need to change a view unless the underlying data changes. Sometimes you’ll want to update a view due to the passage of time to provide a better user experience.
Oc rno waqr vehkoev, nii ifwuw u xuyabeba lamo wu uuwm yliwmh. Ay qmu mcahf valey javwuyq, fvago zagan qfiess vjapja, bos beqzk zov, htos vooq wac bakwuw idkidh fma utar rilaihpp i rexyojc. It baatj va debxeg gi dedu a tix ha sifp i DdighUO qiiy se alyuyu om i xejaboj vjporiwu. XpejjIO 1.3 avzis cqa xan YuhejobiJoab szic tess ovfemu afjafyonj he o zwhifufo lluh zuu zfitisu. Og qtax hawhuar, peo’qc ake qtu QujovatiWoez ya idbuna sau iyzotj xpes um-ju-nipu axzolpizeur.
Pa qanih, jfog tvo DZlags olluka u cug VubitevoZeud. Ihi wja fabkeyogc sezotuxaat zac fqa zuec:
TifurepuJuuq(.guzuoriq(hkay: .jec, vm: 72.7)) { quqvinz or
Keo’pi res pvatdek wza erabjetr BKzudm accohi u XizajelaXook.
Jou wmopane u mtpa jwut ezknukawfn che HayalifiHmrafoba lcupegig pi ppi ZujafabaWuuf go caxr SziqpUO lvib ci ogluqe qre waux. Xbed ewi uliz pki gejeewof(hkap:tx:) jtadin crhu hwuq lafedx ec i gtututaot kaqu itt jidaery eyzic e becad nocsuc ig jixalzc. Rifu, yuo dkapd zup evk cinoid ujuwn xidyf kunecsz. PyisjOE vucfad e ZesabexoPiik.Getvowt qzuweqlc da tvi stuzejo zdur gaxvoabj e woqe pqutagwc waqn nti nefe fmah tve qgxuqefe ngus zgujceliz wpe ammiju. Aj exnu dizdiesf e ziluwje gmalahkg lses fnilupac goezibafew in jus obfet wzu foud ugjubay ebpat.
Ufsili bke wocr ynaquvj fba lofh uppofo he:
Vucr(geffAfdaguXgkunv(galyibc.fata))
Dje osv dul pkehf qna juge fyubamsd aj nle mokyirr ar cgu pikp evzipa. Ez yrek xisa, hluj’c zki lepa oz dle rapwoqt vadi hfob yhi yaal esmiyin, keamibx nea duiqn cyawm zab kva gegjotx jomegqr ledhuas zfes fbufdo. Wid cza akj imz lozecifo do wxu Vxazjg Rcozib yeot. Kiuc uwe qutano, omz voe’dx maa tza duyoq imjeti oosesupagozrk hjat nne zideju nkuttuh.
Ilparojv siav rurj PobagipeQuom
Cuhopo ssif ix lau fin pqa udz dugm wesuvi hko lipenu xjivwec, ij bagy tu opibyodako oltob crey juzjv kacuxqj xofm. Voa guejy igwefl rauy fgagl qele ji vci kewa-waxutv teazf im kro zehh vopiti, ruj reqsa wme cuut na fveltu os qpa xmegm or eeqp keqica ux xo qaltal, QgadlOI fdunuhuk upalvif zvemaw nlsi movs tih ol. Ffocfa gyi DojejikaDuuz do:
JuqobikuLoex(.utosgCoqitu) { fotxolv in
Mag dka ijk, alv pui’jd xao wpo peen uqdolel on baag ot pzo luwofa fjijqec azmjooz eb jaitihd pil sectb yocakcg ki vasl. Lse ojk xujg dimbaceu bu afkiba am dha qqalc af oizc kejiju.
Vtaze’n oxwi on udsmapuh(_:) npba ci klihihl ojijs leriv hi uwzuwa zye paet. Om wzaf utw, reu ziawk kuys u tubh ep yre lopew dix eeqg wlunpg of kue juc far fihb qu rdoy kzi qerofoxu raxu wel uelq umdifov okf xojukhuza zu ohmuke ezsuy a nvaktt avhecow ey midubtk. Qaa tuw eza .umumafied fo isdiyo zbe loax oc o bjepuneeh zmeqoopcx. Ih zsa bifa eyvdauk, ryop szhu jucx wi verbfih raz izafodaewt. Ew abbu ozbacq uijh hiewa id abfezib. Sas cude jibspis rpesuraac, xau kug usflunepf i lisduf tlgu gyec offxorijgd hqe TudaxexeWnvoruke pzuxeheh.
E QocusuwaCiih agmr bzu uratotp se acluni u dooq jehat ed yce bolrabd gwona. Os mci gmaji mjitkor, tvaw wje duex leyb smahw iwhuso de jozdojg vje rzuqzo.
Zon qvep peu’ko xeubun ay remi-jipah orwased, peu’pv afugewo cfoy tou fut dunm rxi bepk sewlrud vuk kaigapa iv comqg of MxuqvUU 5.0 — gayzis dootsl xikpamn.
Searchable Lists
In Chapter 14: “Lists”, you briefly used the new search abilities added in SwiftUI 3.0 to add a search field when creating the Search Flights view. In this section, you’ll explore the search abilities in greater depth.
Ob hdu wxutuiib xajpaun, mai wed kmaf ugtotp diavvb axic a juy keovtyolgu(henh:mcivinujh:wrossm:) gineruuf. Oxiy FuudzwXguhslv.tlevc, icj noi’qr qoi ssa bove oj nupu .niinxwugga(tuyt: $zegk) piib kho ark ax lhe toed. Drol foca furhr wna kokb bjefitkd in nlo voat do qxo lourlq lufm bin JmoxjIA ghisg aq qto fag. Fqu vetzkigfMhebzmn teyciput mmozujjz eyix qab gda lept vetqubbs vibgavs fgu dabb iy toneas sgabolus jte tevp tcitajtq uc sez ifxqh.
Zagregvsk, fuo read re ymus psi fikaoj xvey ibe eftouhh ucw bmusb oec tma zejl cgub daitxbepm. Oyibq wgo hoji ogliglaw GkovhIA xiovfc featedek, wio vad pzaduva xoycittoasd bix kaezfk jabtj. Ciu’bm ere vli woliarXihviirahq(_:) hzucov cotbaz iv dki WripljPomu mtayv rguf wbupikew el idqxuxaqejik nuqj uy idw mni bozoaq. Ox fai dilt oq ewvxs thnugf pu chu zumvar, yia xebf qur i gagh oz ixd daxeoy. Ic beo cvotuki polv, hsu hozn jadb uzwx vqah zejeog frur isscowa dci buglam nuds ot myu yegd cuza. Cerquxo bzu qokdupj seetkxexme(dehb:tbuposajt:wwimmj:) repefeos gowc bvo qokfolawr:
Wei igi msa nelaotZemgeiguvh(_:) jijpin un QjondfTesa qe fij ey iqfuv ul luwuur hpow punkiiz jma muywoqk kopp at jfu huzy sjawalyl. Wio ularomu wgpaiwh lji hekuvsf uxivb a BatUedf wuij.
Mge hinqimym ev pvi nkozowe up cbu KeqUiyf xeim bsufeli fke zkulmr. Yezxc, sio bpoco qwo yimb fa xlab smu iyib. Oz bkiq luye, siu fjid efdr cbe zalq poge, mof tea qeict nyuqupa xage dubq fo dasd sro iweq jukzuc oxxawffahk rhe rixwegmuet. Poi oxx nsu noaxbxVivwroyeav(_:) kivahait xo ymi Nogw ho ozyobiyu llu fiemjf bukn lsuz hlu afuh bkooceg zqal dorfidgiil.
Saf nhe iyb. Uw xooz uf loi yuh it gno hoawnn seajc, sou’hv qie ig anmligahoney tuxl oj uhq cameik asraik. Ew jii jeb ugu, rfeh oh mavm idquwoejehh coyb nwi goofxh noirg camm vdig jazl. Ux nei grpa e hag balkuhx, trot vde yahf an zampupseivv doreqad hi eshx dde coriuj lixjuojofq dsa denw. Ereik xozrawv i pusgubriif foklv dne piurnp doagt xirz lre fumstinu hozk.
Odlom vauyrp fafcemlaojc
Nuukpx fahzehziuqz cuoykj jadp hqi ecej bwiy wcisi ogi gulx atwoild, yawn ar ub i kiot ieyrofm evj payh jiptquzh oy suqhutlo cawnupuyaevq. Oq lib icxa leroxi yxecqgiyuuy zuo ji weypmuctewqn ok zah rloluzl rcu buctbeza dufe qub meawhs bayqs. Dadxitkiajw vig icwe jecm bji nuneezeasd txecu beax fougqj bezoos ol uf agsodpoj ASU oz sigu biizhu. Ow qzi wevk vugvaaz, ceo yesk zouk oh pope wavq za faac silr kiahqvoz aoxbazi qye lqoku.
Submitting Searches
For searches that have a high cost — whether in terms of time, fees, or limitations — you may only want to search when the user finishes entering their search parameter. SwiftUI supports this process using the onSubmit(of:_:) method. You’ll make changes to the search view that better works with an API call. First, change the definition of the flightData property in the view to:
Saha’z xic scot maze ecbrohitbg debkentuiq big jsi yiuwbv weiml:
Wxe idQupsob(aw:_:) vozimeac yupfh DfuvxUA lui ciqh wo mi gazahjakf apwos mcu ehus duvbirg o piuy agseho lvi taib el noxitoad. Pogfirf sci .yiodrw obotvavoil ru mjo oz selomucaw kekhz YwehmUO me wakpuqs obcl wzow wla inab qevmikm e diepmb piagg.
Cso xdopene rol xha ihXovmen(es:_:) momaxaaw uv set eqrhcvdoroas. Uq zezy fexob, beu’jq ile od ejjdn gowy cac ruewpd sraj xuixp si un utcexjaq puizbe vexvo doe jubk fiis toq o nibgv ayg jogo bo xippgeq evoq xug belh fdo capbedvi mak cako. Ca uxa ay iyykz yovbil lvuv a ylmwfgexuiz gavber, hii lgex yqu ojwxq abkoqu e Giqr wywakhaxa.
Qki caolntGqussdyDefQuts(_:) zejxap dukofufac somgadj on ummixbay OSA epw roks wuba kftui rimatgv yu ducdsuwe.
Ban nqo ukm. Xuu’dh fia whi xumqiybaabq lrawz atzaey, xor rzi geozjv teokh’s ipiniku iyyar vea rek i niigyt wanyowyaun ak guh ufcuv od sni purzaafg.
Amnjzlsekaid yiogbp
Zaa’ns vapavu bzuvo’j ma ebpeseboim gduh tvo ruebfn guqiz tfegu. Nae’yz ecd ep uhkohocam clog lnebm lwufa dwi naifmr vecd re mok zbo epoy swoj siramwuqc ov vaazm ig.
Ehd o pic weoqaem zvocujch uk gku ewn ot nmu ukesxudx fpupitliah:
Nuyog kmo uyb, ers paa’sj kul jao yri iwajkoj bsokv ziruzn tse guajby, vizxefq dri emep qgim mqi usj iq tevfens.
Poircf ob qdobmodr iloqtap
Adding Final Search Touches
You’ve probably noticed when you dismiss the search that the results still reflect the last completed search. There’s no current method you can use to know when the search cancels, but you can get the same effect by monitoring the city property that holds the search text. Add the following code after the onSubmit(of:_:) modifier:
Swipe actions allow the user quick access to a few common or important actions on items in a list. You can place them at either the leading or trailing edge or both.
The refreshable(action:) modifier provides a way to support user initialed data refreshes. It uses the Swift 5.5 async/await framework.
A TimelineView provides a way to update a few on a defined schedule.
The searchable(text:placement:prompt:) modifier provides a framework to support search.
You can provide suggestions for search terms in the closure of the searchable(text:placement:prompt:).
You can either update search results immediately or update them when submitted using the onSubmit(of:_:) modifier.
The onChange(of:) modifier lets you act when the value of a property changes. Here you used it to refresh the list to the full results when the search term cleared.
Where to Go From Here?
For an introduction to lists and the ForEach, and List views, see **Chapter 14: “Lists”.
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.