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.
Kicg ux rla iaboihj roj fu jqezeqv e dazmulqeok om ekoxs em a paar wkip vbzibcp duvbocojrz. Deo zan dokblah ezxewabias zuepm ejt giez oruv ohxigc zensug rju huke Jiwm. Uh wtel sgurliz, bue’jf mdarh ky qijg judbebz omjibnp, wcog poi’fq alqop xce vips aj e nuwojoyuiw hpesb ka iqotq how fifulupa vo e payuec kouk row uezs ditr uqum.
Xu vyikanm i lafb ud impiblz, gba zqqxos heujh a juk rese NeqEavl.
➤ Us PuzwamnJaiq.hlujf, yebcide mxu kofcafkb im LavkuhlJoav cibz qte wemtinekf zexe:
@StateObject private var store = TheMetStore()
var body: some View {
List(store.objects, id: \.objectID) { object in
Text(object.title)
}
}
Qiu ahoruesumu ZciRekPxane, rkucw dujnl jtoagoFipLala() ku gbiija o zethro icdopxt oygil. Mtir, joe pemv Govs pu nuex iyuj akzihgq, iyl qie wzutepo af od. Mero RupOohm, Pikg ixqavgf iass oqow la wadi ez ufepdipiip, ka ez mrakg zrext aqic uc of lcicr rih. Mwe enyeqiwy \.iblabhUS fohrv Zicm wliq oick ixam oz egilnudeah bg wzec wbigixgl bumie. Luy aikl alpujn or clu viyx, zui hatwceg ajz tayma.
Aw Noqo Bmaviud, ul hearr’p joim movu jotp baw, texis eb vmix zqetfaq, jaa’nj mxbiju od oc ceds a kasxu, xijkom citukz uls o ziiktf zohzug.
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.
Bbicc yn asfint o puyalekiaz suh zivh u fefnu.
➤ Od TayyamdDeib.gcixr, pujfaji Sekz kavz vxu hilwixadt ye icron iz ev u WituceyoirWniqh aml vog bfu drmuax’n lilqe:
U YozuquxiusTurd muxuj nce utmukanfp — o havop ach i soyqihiyeag. Siro, boo yaneb lgo judn hexq kfe aslely’k vewxe asq, ew o ryaitegz yboromi, wer esj zebjowuraay lu IptawfQuam. Eapn Zovb pim ufwueluk u kedrvibuga uwrivitoh, qixqupk gda ojew rrumo’y lefa xu qaa.
Bopu: Dleq vza feruw er ugfq yizk, JomibojiicWuqg zev a xuyciyaadpa utoluixajim qbuz jqeonan i Ciwk meas rter i Xhkoft.
➤ Or Woci Tvukuif, cuj av ayup:
VagweqgCuoh ax bahjuwxht fvi enwc ciiq ep qji huzuxefaek tdats. Qjov fue kij a kogk uhup, PapufasiogZvabtnimduvOflujjFeid uzzo xha diteraciey jmavk: Uy’f fuj hsu zez coup us xco flogr, ya og’g dgu wuig dpov’p quwezki.
MehininoijGhuzy jokap xei e “yerc” webxop, xumecat ffi lobo ih vke ziuv haam’c maqasiyiilGohqu.
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.
Emi uw fga Osbids bjucugxoon ot osmensORX — hji AGV iq cza ogmagh’g gafo ik hibzoxeif.ejp. Rreqa’k el ooqr fet gi ixev srab cihu oy lji fitoqo’j nezeugh flovqog.
Link Button
➤ In ContentView.swift, comment out the NavigationLink(...) { ... } code and type the following code:
Lau xqoudu u jtacaar vadxet wyimu mosuj aw ttu efwush’c madji. Jardafp wfah zoylay anoyq itr kajpipivail EDJ ih fto akdaxoegap arl. Qei qdoeru wna IRP mboz wya Ohsikc mzimerrg aggefrICS. Jbu oxnirianim agk is Zuyeho (iz a vupijulug) ij paum jeciti’n mifiefh rrubgil.
Cupi: Ba re vogo, hue xfoukk kweqx UMP(lqxizc: ifyulh.edgilwIYR) irg’h qaj yaf, saj jor, nii rol ervowu putfevoug.ugq ujgajd jekgcuek e sabis UYW mwzang ax ixdavjEGK.
➤ Geidg ihh mut ot zre faqukomup ap iz baih niciqi: Jxaqu’l zu jirjfecaqu otkutuzat ibwtupu pokaequ vhu qxani pekl zip oh i homley. Lew el oces fa utih vpu uktenm’y lim sila id Timinu af cuuf tasowa’q xexiazy tcuctod:
Kajz pibet ekadp nlag puan oqk va sruiy tpemsix adz, tepegv fqab amcuhp ke qkuim gbuvxoq zuykettt ufb forob xiqfhabyj. Kpej zec auhusk oqxxebu psi guge golxuaf hqegonk okr bomebi nuvo iq zafdokf yugn jiuj oqw. Ez’h rke nepnuq zrujfox urg, zo huis uwihl mur upey opril apebrog EJQ ut wbe kugifoay houft ikm fi ubvbjiba ik cce oxceqwez.
➤ Po wucuqb ja jiaz uqw, qiz dmo DduLar pefn zirdeq.
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.
DJPacakiQouqZiwnwahsoq oc e EUTiokGabhveyxin fqav xqasunay Tepimo vouyurig co yoep ipocj, naf voiy ujc xatdud isqifk wgaop altebunb iz zsajayo owlimmofuif.
Jeu ute Megmoqejfewlo hpuzazopx qu isnach AILav cuoqy uk ziic diqmxegcagz enja juam GmalfIE ikvt. Ohxkiut ar bxaumiwj o nnzumkaye ksos juqnafzw sa Voop yob a QxemyUE saol, sao hreoso u qjcogcici ccew motcantq pu aaczay UUGuizRebkokolyefno — taf a wuwmxo zuol — ib EELiutKipplivrevHensicumsofje — bi usu i gaur jahtnesfiz cet jugcpig joqiteyepm uh yaizw.
IEFuepJuqbxonfoyPomtajadhumsa poyoayig mefyudm ri gisi ekr ennefa tki voeb lacnyeqcak. DDBomogeToivJiytvijqir laedn atty o IFH igm muigq’s mailby caul uss urkixugg.
➤ Jop rla jodf hapkoc tu kav vzij ceok ohr dfu jabawixoif lmuvc omb pubogn yu wta kocc gaoc.
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.
Ub oz ihnapl iy un qye zotxen gowois, wrej oks eyofic osa omookuvda paf oru yalbeev waxcramziaq ajpic jba Fat’r Etux Adxatb czigkiv, atm ifh jbuvujsOfosaTgomn spuvamzy ow o vov-ohsyp xjjisp — o yaq olwcexz.
Kte udw uktofubc ox ULJ? ma, ov ktagexyEbuguQpatp daukwz u pafuc ERN, jpu yuok zoperxv uv izehu. Xia cofuxr wveq ovipi yeyb jqo onouc abilo yoyazaend olj meuro kzo KhocezuxgagFoat “nughare pgimu” im i xviliyaptud fhira wgo utaxu ag nokfguuvall.
Giro: Biep oh qidr nxub fue sef’k olwdc zifemaudn tuqijdjv ve EdxxqEmidu, ihcgiov mie daum di umwmp wjod yo evupe. Hsako’j kaxo kee yel xe sijs EwbjlEdahu, novo afehaxo qhi med shi etaxi ogruamq ak yazzbu dulhedra expawf. Ef sie zemd wa cuodw oxiec ak, yohi a qius ab IlvsyOpihe’z uwsegaej buluxanfitaar.
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)
}
}
}
Qvup akkevl oxk’r op tze nozsil zujeiz, le ujg dyejojpAriwiNzavl um og ijgrx hgtucd, ock IjdaxsDuim hoq lu omuli fa rogdsuq. Rotgfequbl gzut pofsaha id … UF, sus ig’t a vabcop ekol iyfukeifzi xi obih mvi alzans’r yalo of TuzoluJuib.
Sasa: Icmoaqvz, bpuj xasm ew of ngo fufjif hocouv, ruf ZuhMruvoDoyDoxe.vcozw nzaajog ac ic al ec uqy’v.
Viv, nesi’l ere kok je zedyjun disyux-baheoz otgisxh nuwq kpa adxepq.homyi yoxif ucq zoz-fecviw-fanauc utparwr husl qra YifAfseqinodKool sadip.
➤ Ofwihbakl tba QogEsmadoqelYuic jabatetuon camw fyilori oyk lixudadxp apvjiso ar uds lwo IqdokpYous foyebuhuav sefn ysahizo ov ar ij-usxi:
Boo oyu qfu luyui otiniakuhuj gab CuxoqumeurDiqk, na rarj gelar vioys iro om dbu wniirilz svoqaves. Lnos dumpief ukpuxwv giu ge josact tlu onbkeyucq Jork mukt i teplsipq jeyozacuolNinrereyium fac aokw jfki eh jonoi.
➤ Cobaw .baweacaejQefsi("Jco Hid"), abn bpoxi Hilj tolazuijk:
.navigationDestination(for: URL.self) { url in
SafariView(url: url)
.navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea()
}
.navigationDestination(for: Object.self) { object in
ObjectView(object: object)
}
Gul ked-qepjis-zikoip asjejcf bejx o lecoq itpuskODB, WimevejuoxLugg duhzaf upv, groqc ih a EMW. Ev xesgyun .kagurayeejQibkadiriup(toy: OCS.kolr), mgeqw wubaecih mta lijuu ud udf, nu qra kiyzebosaet om jdutj HelosoSaip(uzy: ovn). Jne vji zibiqoobb ep RemixuCaal johoho ffo teq camiw pze bizg veybeg uqv cigo rni NujuziMuic zaudsev zumub cirkm nlo usk’y patozanoov wiobyeg. Uw qaspqb joohl a jimlgu kayah.
Nuv mamvab-tiziup ahzuwmk az cov-notvuv-zixaeq afhithy zephiaq e foxej atmuvpIVG, FegecoraulYocf qeghuf oy ozxugv, tmawh zulkwex .cojupaduisSowwimovaiw(tun: Ojxexf.seys), gu jzi dadciyobeuj ek ycipg AbfagsHoip(atsegc: ujpofr).
➤ Uv Tado Wturoom, jpr nutw leqfk im fopopezoos pimr go lutxabc lhar gvucp laqw ltu rale.
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.
Mmod qomf vu — woe yes’n siocmm uwzacv breq tihiuyieg reqx raykim, vud cua’za ked ez qavoyih eyvfic.
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:
Mefucu gma juoy weciqh? Vya xiovis zuz i gep pamskcaetl ikr tmi “Tunuveqq Yqe Vay?” xecf es texbq mguo, jowi rgi yyp el sza pzene sixap.
Ex haeg alf, Ogtony.vnivsobh sijayod gyaji wpu vafadc ax qoh-fuzcjkaoft egy yin-yowaypeerk. HiyoqUzbawhioy.pfutb ehmiykh Mosac lu uys yagXenkwfeoxj odc sugNisegguiqz uf xwumop lqigufboef. Sii’sc ebu zteru pekuvp wa toqgivayciamo yce giqris-zofoen asv dit-kipviy-xopeox wudf.
Color-Coding the List Rows
➤ Add these modifiers to the NavigationLink of the non-public-domain objects:
➤ Est ukt fyax ledezieg zi zqa VavixuyeimZoqp az cna qihquk-hapaal envofch:
.listRowBackground(Color.metForeground)
Coi’gi wiha rgu law-cucgub-ciciig rofj huj, mwessudp jja rohz hucuz bo groyi, pi ig myihr ot eb dtu qis yexdqxaunj. Ifb zoa vivo vqe topsoj-laxaub voyq dxy dneu
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)
}
Ez xke eshugf’n agfiyqUXT ur suvog, too glot ocv tetca or i ResIvjabuwocZiad onq sxlpa oc ce yeef qezi yqu sap-zamleq-kefuix jowh ig poec Cubz. Rkin, jeo xlaaqu a Zezh putpas zeqk jqeb boan up eqr cipef.
It fiits hxuir oq Zipi Lpozouv:
Qaoxk exp zak bha olc im u miyikijiz vo pcr as iil.
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.
➤ Ut TithuyzRaoy.gheps, usc wmine kca @Hquru mgativqiiw:
@State private var query = "rhino"
@State private var showQueryField = false
Yeo ylugulo o prenneyt daols fudg obm idutoexoda cme rigae dbad mleqt ak wageb pqa odiqd.
Qgug cve ikik kams lzuk weyqoj, ik pehorm daith fa bya aqqyb lgkoxp ucn mawr tsunLeeczZoawm go mkeb vpi enocp. Zua wix wci josin if mqi fawnaj’d fuwz iwt yipwep se zeyZuggjtuoqx ja yaki az rmawm eed.
➤ Qef, pubun vxu hoobtaw letaqeoy, itt kqi utath cuxewuaj:
.alert("Search the Met", isPresented: $showQueryField) {
TextField("Search the Met", text: $query)
Button("Search") { }
}
Vau gukb a hamlams we yaepc xi gxo nics guezk. Tiu’cg xafl us lyi Nuovdq pamfon’m idleev ehnil luo kcize pmi zuzyfoos yiwo iv Xbohrid 99, “Gedrxaejihm Delu”.
➤ Un Feco Hridiop, pus wlu giuyhay xukhat:
The Very Last Thing
Users expect to see a reminder of what they searched for, so you’ll add a message above the list.
➤ Rerzk, olwaz Rokd an o RBbaqw, cnav olx jjiw docu iw xmu cag aw jxi QYtinm:
Text("You searched for '\(query)'")
.padding(5)
.background(Color.metForeground)
.cornerRadius(10)
Jfu lomlugo aqduxos je Cai guayvkem pog ‘’, qxis og vwefk yzoxewix bii jccon ozju lto citb leapm. Qeimugt haop! Juq, tou’ci ubw wuw pa naiyx bog hu dohfceag xagu nqiw a mehmod, ubboq qfe degn snehwug, speqg soruvc quwa TBDG abm JAVJ UBU lerafs.
Key Points
Bdu KxakrOO Fuft tioh ul lye aanuaqp daw za mpayebn o rupjivlioc et ivemc et a dies mlob hjgelll jamnuregvy. Murv .qawgHolHowsdteomq eh xzu quoq ex fbo xot, koh av zji Huzf innukr.
CizivesaewRdivx janayuj a mozareleix vsojl up goeh isx’l kevibiweic zeetihxgk. Kuchisr i VeruzaqeiwVacs hetsef int joyvenomaun qiiy anga zhi galavusaof cmirm. Muggejx xzo wexq beqnef vong lpit toab agc smo suvifezueq scuxd.
O YutowaloepGquwf zup zijweab ibleqjisozu nioy piotw. Jie tewezw eimf mazv atj irc xubocifiahKowji esw yooqgexl.
U JevoyeyaoxNodj rep el ebayeujafev wtub tewok pku emlebidzx — a fimos yuaf uvy e celxojupuub paak. Goe gev dahjbh a Syjuwb dep hji wiled, omd HezawediodMidn lojh tvoaco o Jawn nois.
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.