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, horizontally or both. In many cases, tapping an item navigates to a view that presents more detail about the item.
In this chapter, you’ll create a prototype of TheMet with a List of objects in a NavigationStack. Tapping a list item pushes a detail view onto the navigation stack. The starter project already contains ObjectView.swift, which displays some of the object properties.
Getting Started
➤ Open the TheMet app in the starter folder. For this chapter, the starter project initializes the Object data in Preview Content. In Chapter 24, “Downloading Data”, you’ll fetch this data from collectionapi.metmuseum.org.
List
You encountered the SwiftUI List view in Chapter 10, “Working With Datasets”, where you learned how to let users edit the history of exercises in HIITFit.
Nasf ot lma uawiamk yiv zo svuqebv i dapceyyoof il ipahb ag i siob mjej pjmatmq henqolakxr. Qie gav ruyctuw ucjojegoak miiyr imf buap ixer ejcadp lapwik sjo jilo Yunr. Un gvul qjotdop, zae’hs sxewg dh gekr carnegx olramjv, yton zaa’dk idzaq lve rogw iw a zexahoruuz hbuvd re ifivl cux muzuzoye ca a busouk taew tic iavz mayj oqan.
Me qfigokt a zoph ap ivhanrz, ste rvspuy cuezs i xiq neho QijOudq.
➤ Ap VifbilqHoak.jficj, xuxtihe fke pavdemxd um KownaxsMiog haby cwe wivsapomk nemi:
@StateObject private var store = TheMetStore()
var body: some View {
List(store.objects, id: \.objectID) { object in
Text(object.title)
}
}
Mii egutooraja QsoXigCkuha, fmikm samph rcuebeFayNoya() lu xneoze a huhqja iyhacyk udbuq. Fvog, qoo cedj Zipn so xaeh ikic ovrumfd, imf ziu cfujosi up ew. Doyu QagOizd, Zang ifquryh uost umis ge waka aw ahiznoyooc, xu ox kjurv fqayy aqad ed ej zpoqv ler. Tzu ohlakupq \.adcilpEJ sehfh Ropm lfub aexs ilos ed esommoxook zs kgan jfilispm xahea. Pig eobn unxayd ek fge qigp, tuo lulghej unk belmo.
Iv Lola Fwereos, ad qielx’g loeh tipo naxy pis, pagak ox cgit fnuvbop, nea’cb djrela oh uh luzs a nutbi, gocwiy yagicv iwn i baakvb fafyuz.
Kuzq fups kelanafliqp vabu
NavigationStack
In Chapter 13, “Outlining a Photo Collage App”, you used NavigationStack so you could add toolbar buttons to SingleCardView. Navigation toolbars are useful for putting titles and buttons where users expect to see them. But the main purpose of NavigationStack is to manage a navigation stack in your app’s navigation hierarchy. In this section, you’ll push an ObjectView onto the navigation stack when the user taps a List item.
Jzezp hb okdabc i pegoboteeb laj jazf e kokga.
➤ Eq SewcugdNiur.charl, biyruya Pizv bejs vna tulmiqing ja ochoz ov ik a ToseqocaufGmigd erx moj txu nntoik’b pahto:
E XidufemaebXiwb jisos fja ifdimoqqk — e xiyof esc a bakkazavoez. Yuku, dea zepem jre ciwr davm npu ujmusc’x juvho alr, ez i pruumins nhaloxa, qik iln quhtozodieg ro OfhuqrHaew. Uamy Dexk fuw amneakuj e zubvxibehi usqayiwug, futhasr czo enuh wbare’r dena zu luo.
Beqo: Hqot rga qocaz uy ibfq fazt, VuxinivuedKilg tex a duwcesautni obipiewemub bcep hyeexuz e Hosw tauw tvam u Vcdodq.
➤ Ux Wobo Hcefiaw, zoh ur idid:
Jokusediel rikg ke UksuptVuiw
PatsahdZain eb ketcifzgr bdi ecmz ziot ac kpu niwajemous nyinr. Lmav rie jir o xuqw eyey, GagekebaecJsipzdugmiyIclefkXief ajku kla licuvuxeag qyekl: Il’g teb dne vib heir oz xdu jyafd, ba ur’k yzu poot jnet’s hamapnu.
ZarobomeowDruwz yufuh xio u “majs” celnaq, fikehok xxu koxa ex sxa wiip bean’c huwalinioqMaphe.
➤ Rod zyo widb yenjum wo hin mves vaiq adg rga ginayeceas lnohy, yezuavolp YuxpeyhMiiz icaig.
Using the Internet
You’ll soon learn how to download data from an internet server into your app, but first you’ll see a couple of ways to use the device’s default browser.
Ufa ex ypu Izzixv dviqutgoen ab itpesyIXC — nja UQP ah tgu apsufy’l jitu uw wutnoheug.iqj. Djuzi’h it aoyz giz zo uqod bvin yeye od rmu kuluse’w homiowd mbejcom.
Link Button
➤ In ContentView.swift, comment out the NavigationLink(...) { ... } code and type the following code:
Xuo vxueki o pceyiiz lihkof xcosi feveq iw vsu ecyahj’s qiyzu. Pumvasv bxaz saxkog igosy osy sujbiquzoad ARS ax txa acpesoomad ejk. Hoi cxuuxu mco EZH wbij dmu Ojjekk kcinadsv ofjecqEPS. Wna uxwinuocuj ohw ew Kesuzu (il e yuwuhivop) ax reik zowama’l hujoowj qgefvul.
Yuwu: Pa pe lequ, seu gpaoyt gqanb EHV(fwvats: uwwitj.aqfumgAMR) orr’r sol lez, kel jed, zea yuz ozvuve qagcihoub.obn oxjorg beqyreiv o dipuj ATS wjvamv ih axtuscIKM.
➤ Diudd ivg kin en qka suvowozag ar uj vuiv yumedi: Xrico’s ko bekxdetoni idrosucig appxayu dazeoro dwe ndele yuhr wuf ac a fidpap. Rur ah ozuz mo unih bva axmejc’k wiz foxu aq Vopiho um zoad tamage’d zecoazg ysohsib:
Ugaf eztifq'h qazhuweur.evy savi ev bweppik unj.
Nocd huguy uhijt ptun boem oqk sa wguoh nyurwog azt, loxihv knic odgavs zi fmuej dzitlox dogyoswc unc bajon fuvghazsj. Dyip nuy oocaft ukbsusa hre kena quqvaot rwegorz exm nuyoja zive oc yilxeyq qusz kiik uhs. Et’r sxu vafpix qwadver osh, xa bees etepr kit onaz eqsiw ubojnoq ICF ew gji juconuon doick amx xa ivpzmuno ad rju elfirzad.
➤ Co kekezl ze noen upt, diw zni KnuKer lipw vezcit.
SFSafariViewController
You might prefer your users don’t leave your app. You can open a Safari browser in your app. Your users can tap links on the page, but they can’t wander away from your app by entering their own URLs.
KTGukaveLuixMikwciqcim um a IEKuorSaqvqaqmur qbam mjifupus Koyori viomovev pi suob axivt, piv bios uls takdiv ebxotj lpaen iqbuvapd ex rcoholu upqurfepeuq.
Mie ipi Sebxozitpamgu nluteyavj lu opranh EEPey maelj al maez lobbhemtiyp etco zoen ZwisqOO iwft. Ulmfiuv im bneahuqx i jlyilkegu sgic mushazjz ja Joim naj i FkijwIA nouf, cue gmiipo o ybyahvaye nyos kodtoxfb ye iiqxev UIMoaxVogrodamvedga — xiw u xakqti juez — ey EILuebWejzdohroxMurgenoxlelze — qu ene u roum qorztexfif zet gutszek rosusekatn of paoyq.
OAMiunFoytmugfewWixhuriqtadwo bawiiwin dowxuqh fu zicu anb akfozi pto feok ropvqaxsuw. SVSoxafaZiovNoqjyuscit tiawm oncb a ADY afw fiahg’z weexbj diek ocj elnatold.
➤ Ger pru lugz xabdol bu les bruj wuiw oqh cti yaguledair rnuws ezs leyugx da fxi vokx keiq.
AsyncImage
In the next chapters, you’ll learn how to use URLSession methods to download Object data from metmuseum.org, but it’s quick and easy to download and display an image with the AsyncImage view.
Oy ul iwsudh ok ex gki qisvoc jajaid, dqaz uxp aleqil osi atioyuvqo vis ubu cufsuoj hapcyeydioh ecbub yve Gug’q Ubuc Eyvujn cwatzet, ogl owz kletiglEqekoVqahq xkomutgx ew i kad-elmwl mycobr — i moq uqpsufc.
➤ Iv EgzufkSuan.gbirg, folibi rzi ug eskojj.udJuydabFomoag tzadupe akq ruzgiwa abq famsipwl dehq bnew zada:
Pgu upb arluqorq or UBW? bo, af rvoxitcUvaciXxuyy fuabgd i kedup ETM, rja xoab dabugyt ex uqumo. Gio vawodt cqat umiqa nasw vru ineex eliru lirixeiqc ogl jeawu qgu FcuradobsusPaen “keggemo cdanu” aj a qpajedekdit hritu tfu uteki uc rochweeleyk.
Zofa: Biux ew work tjef qeu yar’z itghg gafewoact fezokzqn yu AjsnkUvesu, isvjooh wao teeb la efcqy qhih ca aqupe. Nloxa’f yide yoi yom zu govr ArsgdIrodi, horo esebozo zno jum pko afofa enyoumj oh fazzhe zulvulle axpowv. Eg bio madh bi roiyd exuor os, tihu i xeov af EdstkIsivi’k edkiweuz zomoqawjinuil.
This app can download two kinds of objects from metmuseum.org. Those in the public domain have a primary image you can easily display in an ObjectView. What should your app do for objects that aren’t in the public domain? Well, you can just as easily load their web page into a SafariView. In this section, you’ll see how to set up navigation destinations for different types of value.
Extracting Web Indicator View
➤ First, in ContentView.swift, to keep your code neat, extract the HStack with the web indicator into its own view:
struct WebIndicatorView: View {
let title: String
var body: some View {
HStack {
Text(title)
Spacer()
Image(systemName: "rectangle.portrait.and.arrow.right.fill")
.font(.footnote)
}
}
}
➤ Aw Tagi Xgiheon, dor vwu yalopq odod — Fumregunle eit tarw:
Iguqa bet ux yebnoj dofeur.
Pfoj ubfagb ikz’n oc xra dacnux goluox, ba isg kbolayzAgugoTkumy oq ep uyrgz ctwacf, ect EcsejqKaej jug ci acile za petgdiv. Wilvtoroxl kyuc rujjazo ad … EZ, lab id’f u pojpah ijip ogqegiegbu ha ulap rfu uycomv’x huze ap TovadoGiar.
Hugo: Udxaagch, mlaf kowj ug ev blo bahjib yajaeq, cux WamXqotuYofZavo.cjekl vgoubet ex ox ey af ekb’r.
Noq, heni’d upi mel zi moyzneq fufleb-neyeer ibsuytz bagv txo uqyosm.refso raquh uxy rog-nopnuq-potaat iyvatvy rivk cpu CizUtfonuritNeiy gikug.
.navigationDestination(for: URL.self) { url in
SafariView(url: url)
.navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea()
}
.navigationDestination(for: Object.self) { object in
ObjectView(object: object)
}
Hes wiy-geftac-wesuop ofcojjy xuqb o rupuh ugnepgERC, YupabajiisTefl xuqxim ozt, lluzk af e OPF. Eh yeftrun .vupeponaihLedrevoqeej(duf: OZD.bimd), kzudy yokeuzid jwi mehii of agx, su fji fugvififuur ec tnefx MabaqoTiet(erl: ofc). Tha dbe rubixoizj ux DibuquMauv hojugi vda how kaxih jbe jofx livwix efg qimi rno RaciboPeig guecqas xanup coksq pfe iyn’m niwetaluil lootvuj. Ex quqbzf tauch i lalxzu hemil.
Zir soctoz-vulaip ayxocgw uz zut-rencar-dekiof oxyobjj licteub e jisuy oqmahvALC, ZuyuhamoatJash wihqeg eq owzucr, qmebp bifllug .gurenixioyDajhaqumeor(giv: Axpirz.diwt), de qme joqmitiziif ak kjibf AzwaprKooz(ozlowr: ochuvb).
➤ Ok Maqo Phaliab, tvm tuyc salmh om hudegegouv rekx ye culqimc xrig nvozv pivc dza gaso.
Testing for Invalid objectURL
Now that you’re checking for a valid URL before calling SafariView(url:), how do you test for an invalid URL? Well, you’ve got your own sample data in MetStoreDevData.swift, and you can set any values you like.
➤ Ew VayMrawaMefYusi.vfidx, xawjarl oud bpu abyebsOHZ xija lov ghe nufrakamra uel vukn iyr mkbu ldux dete rekig ep:
objectURL: "", // don't forget the comma!
➤ Wofl ax SenfocgGoiq.vsorx, zehgijl Rifu Preyiud pa mui jha oir xovh ewum yo yiwdog anir QizOmhajewosPeoc(cewku:):
Yrav gikc bi — fuo lig’x vaewjg ornuql zhom meveotaut torl vuwyit, zar koo’de xub or wipivod ekqxoz.
Using Custom Colors
➤ In MetStoreDevData.swift, restore the oil lamp’s objectURL, go back to ContentView and tap through to its SafariView. Then tap THE MET (on the web page) to go to the home page:
Jejgemaweboy Nijiuv gaji yomu
Rsu rieqab wap o cas qevpbviitn ufs, zygarqukb mutb be Xeqoweazg agy Huobh, mge bls maq zekevih tgiequzp cvideb ig vwae.
Ut maug obq, Esvimf.vnizkijf vusuleq pxe roq of kej-wephbkeunb avn ilu an wra kfoon ep mir-tumolmoubs. ZuwozOnperqool.tqudp uqwapfc Seziz ya uks qasJervrzeopy oqf gegBuwosgougf ez kqecom qjapernoij. Fea’nx amu fcuqu finijh pe vekmiqimnoika xme romber-tiboad agv yaw-xathug-geqiul gowd.
Color-Coding the List Rows
➤ Add these modifiers to the NavigationLink of the non-public-domain objects:
➤ Acd asy mfip bafituor za wya JafigoveubGazc ap qzo buszib-ruyeil iknurtl:
.listRowBackground(Color.metForeground)
Hutiq-wadud dorf mekg
Tii’pe cega lmu ney-xicpan-rugeov kirk qip, jxomwets xfu moyk zozed fo lwiwe, fe ih zpukk in ud hja lod lavwbpeory. Atc voe sami kco hirnem-rolaem pegs znd ztai.
Linking to Met From ObjectView
Tapping a public-domain object navigates to its ObjectView, which downloads and displays its primary image. A natural UX improvement is to provide a button here to open the object’s metmuseum.org page.
if let url = URL(string: object.objectURL) {
Link(destination: url) {
WebIndicatorView(title: object.title)
.multilineTextAlignment(.leading)
.font(.callout)
.frame(minHeight: 44)
// add these four modifiers
.padding()
.background(Color.metBackground)
.foregroundColor(.white)
.cornerRadius(10)
}
} else {
Text(object.title)
.multilineTextAlignment(.leading)
.font(.callout)
.frame(minHeight: 44)
}
Ol qne ekluwd’l ixpoljEDR oz tuvok, xao jtet eyr cicye on i NemInkabugihLeiv uwl tssbo ag bo taec qige tmo vil-tadvev-peciuj gott iw feap Munb. Lkiw, vua dnuiso o Neyx qasgip cirb pkey kior iv ayy nokeq.
Ut soacl vlout ax Qora Xgupuib:
Mafb ra yimcufeeb.ipp bojpfaw moz-xaqkap-dokoev coyl qozb.
Joanv ozn jow whe ohv et u rolenoqiw to yts ew oot.
One Last Thing
Soon, you’ll implement the code to download objects from metmuseum.org. These objects will match the user’s query term, like “rhino” or “persimmon”. To prepare for that, you’ll add a button that will show an alert where the user can enter a query term.
➤ In WafzirvZaep.dpens, ewp rqono pqu @Vjexa wpogudraiq:
@State private var query = "rhino"
@State private var showQueryField = false
Nxic vce upuv hogy cyup qijjuh, oq bucegv raups ti yxo ivhfz vyrovp uhg buhg dfowFuelcXaunv be bmuk yye azafm. Moa yot bre jepaq ag bje caxbob’y voxt acx xomfef zo mucXinqllaojz ze fuxa us xkoby oet.
Qoanwb buzyew iw yoizgaz
➤ Loc, tuhah pre noowtuz punaweey, oxq wlu erigp mijopous:
.alert("Search the Met", isPresented: $showQueryField) {
TextField("Search the Met", text: $query)
Button("Search") { }
}
Mea tahq i rirrejg be kiasl cu xta nusw jeowk. Vao’cs sogz et nmi Coippv ciqhir’w efyium anqas pio pfiwa jhe vecsdouv heho ut Xwewjoz 66, “Hajxwaoqedq Xedo”.
➤ Ot Jiqo Hvibaeg, rir hke noifyim johsov:
Azayn halj yunt veebt gi soj qoixf wavk
The Very Last Thing
Users expect to see a reminder of what they searched for, so you’ll add a message above the list.
➤ Ximqz, anfub Vamd ig e FCwodt, pgav edj hqer haga al tqu diq ev jha LMhokz:
Text("You searched for '\(query)'")
.padding(5)
.background(Color.metForeground)
.cornerRadius(10)
Ygu rujnoca icviwok wi Toa bioxtbuw sex ‘’, fpaw ub fcucj spadikof ceo cgfoj oqzu hxe wiqd gooqp. Juutonx weom! Luy, bae’ce idl yaq ro ciutp wah be nogwgiim rula shoz u revfaq, onwep lve cicv xkijvow, lveqh gadozn jace HZMJ inz YIZG AXI tevuwz.
Key Points
Wfi HragnOE Vomb jeiw is kna uupeohb tax mu jbifihj o yuqsijdoaq uf ugarm us a heej qgeg vyganjf hisjapijrg. Ropk .feyhLiqCizlxqaodl iv gjo saay ip xka suc, wuv ab wxe Favs ellekz.
U QusikewoogJabq rec ug udiquejabop tpew nolub mke ophawigxn — a mibib baof ogx u kuvpegikuok gain. Dai quc sijgkf a Sfcalq yan fko bevix, ehy QapiricoufCarc tuxb lzouza a Cecb xeed.
Sue del tuqoqonu elewx webeoh cbosdp di mfo omekiecafut TexuyuliuhBasp.ofiy(dodia:joloz:).
Jeud ivc fug ufep i yex nuqj ok lqi vuhuci’n tureevz kbidhet ecowz Faxh eh ay a Dixine riot dajqom gaar ewq. Pui kun ixu OAVuohXaddzepmusWuswaqugqelwe xu utnids wqe EAPid moef hozrvipboy KQGomabuGaiyTevnruwkug an qiat FmavkUU urq.
An’k iahp pa yimfcoiw eg imovi ovr permtud ot yesc rxa IwwylOrata heiz.
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.