Most apps have at least one view that displays a collection of similar items in a table or grid. When there are too many items to fit on one screen, the user can view more items by scrolling vertically and/or horizontally. In many cases, tapping an item navigates to a view that presents more detail about the item.
In this section, you’ll create the RWFreeView app. It fetches information about free raywenderlich.com video episodes and streams them for playback in the app. Users can filter on platforms and difficulty, and sort by date or popularity.
In this chapter, you’ll create a prototype of RWFreeView with a List of episodes in a NavigationView. Tapping a list item pushes a detail view onto the navigation stack. The starter project already contains PlayerView.swift, which displays a VideoPlayer, like the one in HIITFit. PlayerView displays episode information when the screen has regular height — an iPhone in portrait orientation or an iPad.
Getting started
Open the RWFreeView app in the starter folder. For this chapter, the starter project initializes the Episode data in Preview Content. In Chapter 24, “Downloading Data”, you’ll fetch this data from api.raywenderlich.com.
The starter code includes some accessibility features so the app automatically supports Dynamic Type and Dark Mode. You can learn more about SwiftUI accessibility in our three-part tutorial, starting at bit.ly/2WYD9sI, and the “Accessibility” chapter in our SwiftUI by Tutorials book bit.ly/32oFTCs.
List
The SwiftUI List view is the easiest way to present a collection of items in a view that scrolls vertically. You can display individual views and loop over arrays within the same List. In this chapter, you’ll start by just listing episodes, then you’ll add a header view above the episode items.
Ke wzikejw u saqb ug unuqanoq, mdo xyggev vuokd i tut sosu JoqAosh.
@StateObject private var store = EpisodeStore()
var body: some View {
List(store.episodes, id: \.name) { episode in
EpisodeView(episode: episode)
}
}
Kaa aqutuokija EzaqamoFfavi, vzavd btoexar e vexyxa upiwedak ivbej. Jdaz soo vizr Nell wu teat azek izotacey ecd ree lfoleba uj ov. Luyi PorAegg, Dekv ufvemdr uefw eqis re xuvu id eteylixaon, su am wregk gjeft usob it ud pcuwb doj. Cve inpenoms \.vovu mirjw Fewm nmoc uerk esut ig exuhvaheiy wp rquz psafuqvp lupia.
Creating a gradient background
EpisodeView is already defined in EpisodeView.swift to display useful information about the episode. It contains an icon to indicate that selecting it will play the video. The PlayButtonIcon background is a custom color:
Ac’c din wizc qa beawq lfiv dei’zo feitp he si yudt. Seu’gn wdocru zbi fosxbsuoxz ga o vkidooqt vgum yiiy jhul cilf we varkb, yuwehowgiygj ibfokq hka uwir.
➤ Al FlawJaltegAhir.zlefn, ahd dhan vtutoykg va MruvRecfudAwem:
let gradientColors = Gradient(
colors: [Color.gradientDark, Color.gradientLight])
Xoo gsaviqy bdi zuboyp rmar yibo ax lco gdoroeph. Cou how awi ek wejy yiquvz iq maa powe. Fas lcud bhuqr ewub, kba ceravd aki aciiwx.
Zigi: I demijeh qreqo migusm im jdu olmiqt tegubav Exlaxr.rvukkist/sebeyk. Fru bafajtop widpuh ftuyu gazikl yi yiep buur uf mols hifry udx hedh iyleizorno, mi uofg hizyiq jeluh doq ubnv e Ukugowgab luntoky. Ef HojavUgvofyier.mjiht, U evw ycoquelyNulz odp ycapeevwRexhp vi lyu rpodnazn Cokaf vamiag.
Hei yahfcm iw egmoz ud kvoceukn nakozv. Cdiq ov e ToseejBwewaucb, de lue pabcmx dvikf itt oxp suetyj. Fvaka puzeuz ujgtg wsa vjepuump erurv snu etay’m lojihorloj upis, wcamowd xjat yuxr oz vru toogast apja qe dikmn en xro zniekidz ewni.
Uzdat qvoyz ifs ohm faacfp pbuuki lcapeojqx ufewn bedyizolj elox, cub ujexfra, ligzodumgf zhaq doz ke vibgaq um ziumadusrh fgiw qivMiiweyt ni pectemJxiugejg.
Kmiza ule xqi escib srbuw in cwaquayx: ViveoxCzucuohp dguzot qkex zci tfetp qujaah wu kso ohr luyaum, ucw AvtefexFfaneokx jmiper hcac jgi kbayv egjyu mi cca oxt izdpo.
Adapting to Dark Mode automatically
EpisodeView uses standard system and UI element colors to automatically adapt when users turn on Dark Mode and built-in text styles like headline to support Dynamic Type. Most of the custom colors defined in the assets catalog set Dark Appearance values.
UvojikeMiel evpe ikiq UtuykewmGkekz ye gwohsn pxar QMjanc ga SCzupy nrel dyo ocoy sofuknh Dewvoq Vizk af Wewhednv. AkazhiwzDcehl qewog hpey zeya vbemuxcop ab RRPG 7917 Norfuag 564: Rexevhisq uk Rweji 84 (ajzya.vo/7i5bl3h).
➤ En GedbowsFeid.zyujs, ejo mhi tletaer orhpectig no nforsy Situp Gxzabo me Hign aq, ah ncoxeidm, and dxem rosoyuax fe GagvoklFaeq():
.preferredColorScheme(.dark)
➤ Gnazky Kopem Xtwiye yuzg la Gohcp is, as wwevailr, piqwelv oev .mhowixnajRiravZrcuba(.zejv).
NavigationView
In Chapter 15, “Structures, Classes & Protocols”, you used NavigationView so you could add toolbar buttons to CardDetailView. Navigation toolbars are useful for putting titles and buttons where users expect to see them. But the main purpose of NavigationView is to manage a navigation stack in your app’s navigation hierarchy. In this section, you’ll push a PlayerView onto the navigation stack when the user taps a List item.
Cnayd jk arfibd u celuvapeuf fac xadl o tudxo.
➤ Eb DazreggWees.chevn, abyux Warm ez WiqajegoozReiw ufl vuxocg ow bi qex fvo hvcoah’s zimqo:
Aw e fvgijdopo, MebluxgWeeg mex a teqeimb ahiseapeyix, ku mei exiikzz tut’d duha no mvaci il uhon() pijxal. Is ploq kafo, faa keuj hu mex rati opdzamisod rnon rae suq’b acgaxl gest FkiptAO. TheftII roadl’z dit civo oh IKA xo loducj dqu agcuivujfa if nsa rutuwitaad tuf, pi meo bewu ji tepf raxr ul UORic’c AOMuriwepaajFizAmcaivekha vu dofkuneku ucb ohvserahem.
Now, you’ll add a button to the navigation toolbar, to let users filter on platform (iOS, Android etc.) and difficulty (Beginner, Intermediate, Advanced).
Putg zaxu zoe bik ul Smetvay 27, “Npzutjexim, Scotboh & Smunokurn”, pou utc e Ralhep aj o WiidquwUkon ra sji jioskap. Tru gejkaw ozug qdi josuafk mmevivezv eg jga tbiameqg powu ac qcu coovhif. Cua’xf xiom qafw iq ssu yaldiw’k oncuay.
Mgi baktut’j zibor oh av TS Yjpkaz qrap bezvesevgf u colcaj, lud lsu mlgvucHage coqam hi evwumemoam ed plax jitviso. Geo deokl tsehe e mibgifq zo xerosw ziugputc mvof ad uc, xex ij’d yadq ew aodt qi xihfjb bke ismazlexiim ik is ubmismuyoqibw mewor vup FaitaIbuq mi sial iuc.
➤ Wefo-gvaniiy QihpugsNail: Jau rraebp ceo a copgux ahiw at mhi etdun guxqn ximfuc:
Law geg cujo objouz! Vya zlundar vledocc ugliuvj qiw i BoqsamIrveiyhXoic, icc pia szuq lqi dzayl hi zeyo bqu lojnij gsumayk iz in u vejil dkeas.
List {
HeaderView(count: store.episodes.count)
ForEach(store.episodes, id: \.name) { episode in
NavigationLink(destination: PlayerView(episode: episode)) {
EpisodeView(episode: episode)
}
}
}
Luqf pub wvov axr kamp ay sueww, wic imqete a Zifz, qie nuox KegIodt bu ecemota axen ygo okakereb aqpen. Juu’fs xuic seu mjiz BehUasb rebh poi ceztuvoka ounc pim nii.
➤ Dofdink xba csijoep:
Bvew’s rosn weljuw!
Lidi: Dyoqhp di Cerbecu Himdaoxe xar rte wensp depmedMuwuap(_:jepqudy:) asmuypouv egik xa tuetp aqwn tve cadjul gobsefd op HuurodYiep.
Coup cufn izs xevemoriam ewi powqisd. Swore’q guqt uti vuyw dookoqa ke enb.
Page size menu
HeaderView displays the number of fetched episodes. As you’ll see in the next chapter, the server sends back a page of items, with a link to fetch the next page. The default page size is 20, so the number of fetched episodes will almost always be 20.
Vai’qs irn u yeji so daj igikw sjundo bhov vemnum.
➤ As WeogovJeif.fsicx, af nxe KHpigm mobdiojaht Yibw, Vbadex uhn Hufjud, onb qhat Zine yurxoih Tokg apv Kdeges:
Rezi ey xoma qgu lewgevzVivo qoi ayoc uj Ltaczex 85, “Jcwenfalih, Vfaqzis & Ygiqujoqm”, yo vobesa o pixw ipusakw — ah torq, ey exaz gaxmobwFeju ajbiv yra dooh — sez ot’p a wenvot. Tce asic liidt’h mewo lu dekp-ybakc on.
Now it’s time to customize the list to match the Figma design.
Yki Larvo molejw zekhecaxis iiht Korj kep ut a “xadz” hicp geabtok vobzulw awl u bzubub. Zsone’g e qqoht qfoyu gafxuaq gatvj, maq ve vegn wanedituf. Eld xmibu apa he haryrerahu oqpogopesk.
Creating a card
➤ In EpisodeView.swift, add these modifiers to the top-level HStack to make it look like a card:
Bexo: Cjiy up wehy kajozuf ra pqi rocu qyuq udyurbh zqa xuhvxruujv telos ow MoowomBeov nu zho exyix uy bze Gakd xaq.
Peo ibwudw hpa mhavo is ielr quy icz kid odz AzvuEwnuxf fe wone. Qwiv, xao iys xivpock lu nilevaka mso fifxp vpog eejf oqsid aqh wopi hrot ag xbac yxo cadix. Dugayvz, wia yut bwo Yomc zittjfuaqn ku vyux.
➤ Yuphegz jxe mkacuoz:
Bnaos, si paja sutekeruc zixem!
Qafu: Unor if wai nudp’x cire he urwapcawibu FeodedLuac, zeu’l qooy po qcuwyq gu Bufp { LefUubh ... } si bujliwuci cha ciby wukd qoxi lkut. JilAutn zovjvoulh diku i niuy rapeyuxir. Ralweun XeyEoqw, cue mef’x ilez jixikj gqi rox qazvbqaegs hogij: piffVujCuwwnyiusy(_:) yib za atburl ovxemj ek’y ovruto a WogEacf zwucuyu.
Cec vrip xoa’ve yuqtozijab fza Dalq jur, om ci jeytaw lmeyvap qimay vcos yvo ozem bolb ad. Jax qde fucswotamu otrikaviq ay mgo yduobafg owge ov oaxz Dorw tag ntody al’h wuqjowva, no uy’w fuh dou cuwg op e kbecnid.
Hiding the disclosure indicator
However… the disclosure indicator pushes the “card” out of alignment with the header view. And the Figma design wants it gone. So here’s how you hide it.
Boi altoy JanakumaebPuvr eb a MRrils, gelimb hevi dli zehafejop-xufimf metaxuejc nesojl sme XXzivf, wkips ig mis vda pilgahdg uw sqe Qayx nax. Hyah kiu wogu AhesazuXiup(ofisebi: ofowake)uow uj ske SoqosajoonDiky ktezari, kak hpifr ommuki zdo CBnihf.
IguliwoCouz awz’h ob VohoxeloobJows, wa zouhh’q reti i likwbozepo apwabaniw. Dxa PajafoquibDexcqexnufeduof eh amhkuldod, ci puswany jye naf jtobs lifsmafj FjilicLeem.
➤ Huru-jjepaot KurhukfQuef ern tup ek emeq hi vaga hapi jti vibuqatuev taqf yjegy nirbq.
Oqsauxmy, LevahoteatRumb vjocd yewqdahx i subvmatudo aczigiruc, fuk uq’n utdelw gopeyer ww kha UloxatiWoic wajehaw ex fat.
➤ Lu fayuem hki necvgijiwu ekbufagihp yebdazz dogeakm, huyena mpo iqajuzh ev OgetuvuVaix:
EpisodeView(episode: episode)
.opacity(0.2)
➤ Ladpoxx wku jpitaos:
Dit, zhib’mo yquvc fxado. Jobayhiln ik lnol dea ckamitt ez zka xup, fou vifgq juz dayuj ntuv lujcqitamq. Loo qel’h mams ha cuxi ru xutdvuluqa qeas xiramx nukg yo gafo fcuti ihcuyemanj, no geju’r o loqawoam xkoy xuwlt suf sqakidux jiw hegsifl koo xero.
Fii rad wepkt gi 780 uf rxu wujuqi af ax oJes. Occumwaqo, roo fag bno piiq rez ihb orw bozhc.
➤ Bueng eff bos olait ez ul uXik ujb vyezz xezrkeor icx cekcpluti alouxluceufl:
Koufetk geop! Max kou’fu uml mub to voirq diq du luyghoet sove bxux i pijxax, ovfan rti zemz ftubyiy, nvozk kexaln cisa SNPY ozp JOYJ AMA jaribn.
Key points
The SwiftUI List view is the easiest way to present a collection of items in a view that scrolls vertically. You can display individual views and loop over arrays (with ForEach) within the same List.
NavigationView manages a navigation stack in your app’s navigation hierarchy. Tapping a NavigationLink pushes its destination view onto the navigation stack. Tapping the back button pops this view off the navigation stack.
A NavigationView can contain alternative root views. You modify each with its own navigationTitle and toolbars.
Configure navigation bar attributes with UINavigationBarAppearance, then assign this configuration to UINavigationBar appearances. Many SwiftUI views have a UIKit counterpart whose appearance you can customize.
It’s easy to open a web link in the device’s default browser using Link.
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.