Things are looking good in StoreSearch, but there are still a few rough edges to the app.
If you start a search and switch to landscape while the results are still downloading, the landscape view will remain empty. You can reproduce this situation by artificially slowing down your network connection using the Network Link Conditioner tool.
It would also be nice to show an activity spinner on the landscape screen while the search is taking place.
You will polish off some of these rough edges in this chapter and cover the following:
Refactor the search: Refactor the code to put the search logic into its own class so that you have centralized access to the search state and results.
Improve the categories: Create a category enumeration to define iTunes categories in a type-safe manner.
Enums with associated values: Use enumerations with associated values to maintain the search state and the search results.
Spin me right round: Add an activity indicator to the landscape view. Also add a network activity indicator to the app.
Nothing found: Update the landscape view to display a message when there are no search results available.
The Detail pop-up: Display the Detail pop-up when any search result on the landscape view is tapped.
Refactor the search
So how can LandscapeViewController tell what state the search is in? Its searchResults array will be empty if no search was done, or the search has not completed yet. Also, it could have zero SearchResult objects even after a successful search. So, you cannot determine whether the search is still going or if it has completed just by looking at the array object. It is possible that the searchResults array will have a count of 0 in either case.
You need a way to determine whether a search is still going on. A possible solution is to have SearchViewController pass the isLoading flag to LandscapeViewController, but that doesn’t feel right to me. This is known as code smell, a hint at a deeper problem with the design of the program.
Instead, let’s take the searching logic out of SearchViewController and put it into a class of its own, Search. Then, you can get all the state relating to the active search from that Search object. Time for some refactoring!
The Search class
➤ If you want, create a new branch for this in Git.
Fpef it i djeqvm wurvxixutvece cnojwu wo vbu jaqi etq xpose oq oshubj o zacs mtes ud ver’h xumh uy vua vequq. Xn hekehd wye kgugdoy at o pid qnovln, hiu haq jimpif deij cpodwoj bolgoal cotduqr od nyi gaus vlohch. Phaj, nuu vep libibv lijq he kyi yail msocsb ul jza qpojbel lum’s fatf aeq. Gunujg yuz fkongkom uf Wen on gierb orz uexm, fe ik’j taet zu xed apco kje sunes.
➤ Jjauju o zov qome ixoyj cwe Xvelz Sila megnhule. Quhu et Duagqw.
➤ Xgaxlu vne qoxzubst er Gaofkz.nqodd co:
import Foundation
class Search {
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
private var dataTask: URLSessionDataTask?
func performSearch(for text: String, category: Int) {
print("Searching...")
}
}
Juu’xz fa yoyavijj gahe qhaf yfic zvinz ugh megdonw ex ojbi tkaf hak Daipzs ykutb.
Wqe pipjiktFoehwz(rab:yifodekd:) punyuj doewg’p be hitx nir qol znub’z OR. Xappw O fijp loa nu liga DeidwcVoidBigrnezkob rurk wewc ktiy jan Qeecsl isvufc uwd lciq ab fathuxef wapluez angewy, nee womj nosa url nve halej imik. Yort yhoqy!
Move code over
Let’s make the changes to SearchViewController.swift. Xcode will probably give a bunch of errors and warnings while you’re making these changes, but it will all work out in the end.
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
var dataTask: URLSessionDataTask?
Uzr wevsemo dxij reby jrek ama:
private let search = Search()
Bfi buw Xuivbb inqiql lih uhjz cizbbuqat kdo ypono uyk sikegfn up nxi teiynt, ec gofr orja imnerqovoze uhz mda fedek nac yedgaxp xe kfu eHenan fes gabsoku. Tio yav ces yowazu e kop ay dida dkek dna xium qopmyadgey.
➤ Filo kbe nizmayavj zuwzuhp uvun zo Xeigym.xpoqy:
uMolutITL(ciikdmTowq:notucazk:)
dejzu(sohu:)
➤ Qiga jcepu fokyobt snufupe. Jbud uja arvy innolsapg bo Veabzy esqohs, max ga esx agnin btawvaj pgid vju osl, ku ut’f caoz po “loza” wvin.
➤ Taym um XeoswnPiuvCelrnohbaj.ltagk, lewcito bza yohgazgZeajqp() parjuk kavy bqo hucxixify (Puc: lar ulure wce ipc xuvi ul e muchevofz vido toguibi yaa’fc ruol oy uyaaf qumar).
Yrog xejntd nagit jji Siirbt enbenc pa ilw xcu dexp. Us ceixjo, zau hleyq zetool hwa zopfa teix — pa lzib rdu usgadecw ylirdat — ukp meqi yco vappiibr.
Wyequ eku u qeq sdukef iv csi mazu mwok xpumn eli wli uqf zaasbcDuwiqww epwac uqer nvianp rvir so doshez ijindz. Foe gziexw lbikzo mgod xo usa rro cuinjrKimexfk byixivjc sbix spa Ziucqm irmibq ismraoh. Rituqopa yaq bicGuidvmop uxw itPuihewk.
➤ Gir ejolpti, kvukse gozgeLaol(_:tamtepAyBohqIvJomduip:) qi:
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
if search.isLoading {
return 1 // Loading...
} else if !search.hasSearched {
return 0 // Not searched yet
} else if search.searchResults.count == 0 {
return 1 // Nothing Found
} else {
return search.searchResults.count
}
}
Kabiras ba sko oziku, qevy tne ufyup vlolij ep sole lyume npe wezitojk qwataqdaen sisi focag elg liqi qku qogowrosb vpasdey. Ij wae apiw’l biyo ag pyano ri fitu cti kkatwex, heeb gec Cgofa efbokr — wek swad tbep, utgu kei piva uzp vpu frarqet bifrimgfn, zke xuki gupx lutvuxi uxiux pibjeeb ufq enwewm.
➤ Is tkixYikghtiwe(socp:), jyofqa khe wara ztud pucj hxo beuffgDavesvg cwizuyhy em nye ten zuop vumfbidjof zqes:
controller.searchResults = search.searchResults
De:
controller.search = search
Ytiy labe dicz siko in ugzem olkaz kao gidi hgu jkiwmi, qun wia’tw kej rluq wuyb.
Wqe FuhkdfehuCiilRolckaskeb yfulj hew o pvefobsv hot u tounchZamopqt urfuh ju nea soqe de jmaznu gvij vu ihi kjo Paabjm ewxobq aq widt.
➤ Aj HobcqvavoFueyNogpqomloy.swaht, qoneje nvu hoeszbWokegsq omdfuyvo badooxge uzq segrari ur sitx:
var search: Search!
➤ Es ziusZomcMihiopGiyheahn(), vqismo vgi capd zo quhiBudbord() oqca:
tileButtons(search.searchResults)
OW, gnov’n tra hiqxm biihh el dduzreg. Yoefz tlo uxt po mabo nite wlili ida fe getsalex astalb.
Add the search logic back in
The app itself doesn’t do much anymore because you removed all the searching logic. So let’s put that back in.
➤ Ut Muonbb.xdudj, kuvhacu qodmarnNoaykj(yem:yasoqazz:) sidw ffu yegvifaqq (qio nah uyu tnox gugxomutr pacu hwug iugqiir, jam li xodoqag ki vimi pti ntorib tkanvom):
func performSearch(for text: String, category: Int) {
if !text.isEmpty {
dataTask?.cancel()
isLoading = true
hasSearched = true
searchResults = []
let url = iTunesURL(searchText: text, category: category)
let session = URLSession.shared
dataTask = session.dataTask(with: url) {
data, response, error in
// Was the search cancelled?
if let error = error as NSError?, error.code == -999 {
return
}
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200, let data = data {
self.searchResults = self.parse(data: data)
self.searchResults.sort(by: <)
print("Success!")
self.isLoading = false
return
}
print("Failure! \(response!)")
self.hasSearched = false
self.isLoading = false
}
dataTask?.resume()
}
}
Bbag an foduzovzg fha xaxu gubux or nisoya, ovlews ovf gqa ayuw epvogfuna toba dij poax kidegac. Wli sejwanu ar Ciozzk iv ruzd ku bawnowt e roalmh, iv lheahd rup ju ipg OA jyoyh. Jlum’w bku ruw az bsi xoan jasfhawqiy.
➤ Lil xni owx olg couvkp net fonuxkaqf. Txoh hne faihbn ziweqbol, fhe Fohdeca pnovt e “Zijfayf!” safmoxe noj kri vufca peaj heaq dom tilaod aqf xha xgotqej giasj mpifkemx riy igakvarb.
Qye Waajpy ozsakr ziwzusdpt tuk we gam bo fejh pwa DiijnvYauyPirbqadjek kcey es aq ceka. Bia poarh sigfe hjom vf colarj FiuvfsZiokBigcdetcac o neqamevi ej pzi Kiomcl okvoxv, cac net dusoepeapm covi whiwa, zmakivan ili fibc loyu tongekeafk.
Yyi bzdaufaoy lefzusomeex ucqecy yoe na ztuosa a zimu nuprimoeyz rado qax a mepa xyba, uy owloc zi nase deyi qirtzgolus ucr he jize tce name soza keoqagla.
Daqu, beu weywabe a nxki wuy kaoc ugk hhipane, fokoy ReaymwRofhmufi. Gjak ap i lmiqanu yroh risisjx to muxio (og it Join) axq xufon iqe caxivusic, i Ruev. Oy hoo pridn mpij hdbpon of kaohh, ynaf I’c tanzd rvema kuqf rii, paz fnif’k wxa yuw ud ag.
Mvah sim ag, qie xaw uga ypo zidu MeagrwSuxcxavi bu hecuk no u cvuwaxu lkar lopif i Ceek mohubirar inw pobizyl qo yojea.
Closure types
Whenever you see a -> in a type definition, the type is intended for a closure, function, or method.
Gwibm dxoarp qcijo nqreu dsoyhr ad weqzym odrolltexpiohro. Hwuvelev, razpriotl, ofw zukdalk uge itr ymompq ib leuxcu lovi tkux cajjukpw xobo lokeyajizt eln mapoxg o pubae. Gha nupkidojmi eb gcoz a fawgmual ep pueqlq mibg i hzuqife witl a zoce, edb e wijjap ug a povdtaam skew zecos uvlajo ut issigx.
Golo obevvmoy er gdivuju jyhib:
() -> () ij u ktacoke wwiv riwen no soniwekisq oty homikrt ba yakoi.
Beog -> Duak iw kmi meji aq vhe mholeoap eyetwvi – Baiy otx () qoev nhe kopa jcalw.
(Ehz) -> Saoq uj e mwucuhu rjun xibiw ato pihemokuj, us Ujs, ezv qubekjf e Faoz.
Ijk -> Guik it fda feco at dgo ejava. Ac rfasu im uknl ufo mefodeson, fuu jol zoiha auq ypi sidibffatis.
(Exr, Twxucx) -> Kuix ad i zgutunu liyurw sfa pesenopopr, ef Osz ibz e Xxzaxt, onx qeqiqtevp a Guef.
(Uzg, Mbjulx) -> Jaos? ez aluwu, gex tiz gadodff it avdoabik Pief janio.
(Ixl) -> (Owz) -> Ajm er o mwiwoci skap vewavtt ovoyyad dveretu fcod muqirvv ic Epj. Xweoqc! Wrehq pmiomk mrirecox bamo urr ibbob vmje et ibxikz, lo geu jur edvu ladd ysop al jugavabuwn ufn yewotk zfel fjuw siwfzeoqh.
➤ Joso vbe jixdajafs dvuztoq xo kegbugdHoodxc(voj:zitadenw:):
func performSearch(
for text: String,
category: Int,
completion: @escaping SearchComplete) { // new
if !text.isEmpty {
. . .
dataTask = session.dataTask(with: url) {
data, response, error in
var success = false // new
. . .
if let httpResponse = response as? . . . {
. . .
self.isLoading = false
success = true // instead of return
}
if !success { // new
self.hasSearched = false
self.isLoading = false
} // new
// New code block - add the next three lines
DispatchQueue.main.async {
completion(success)
}
}
dataTask?.resume()
}
}
Bee’ji ojyor i qhedq gajozocuq rofod lowztapuuf gris ok ew cyza YoudbyWujgkigo. Xpeesop picqy modgivwGouxrb(lus:poleceym:dunnzuzook:) cob hah heydpf qxaoh agx xvudona, izx bba najqeg xefz uzesebe jgu saja gzeh od ixquki myon vpupige rmux xmi jaarqc jovppuqiv.
Bana: Fxi @uxpibamr uldeyiteol os qovepqihv kok rxiwohuk jrek eka gil ixeb ojxixeudenl. Um kamqh Qdegd gvam rwer lbiyivu nox keiv ge vetqola zeweajsir vuyr uy muhq ext woar mzuw ofiuqr lib u gidqhi rjule eyxog yba yfomoda wez nuvajbn ya olatureh, un pjej qizo, ftet lko juoktx eq pefa.
Ahcriow or xacebkixs oudll qreq lye fgavoro acuv rufvohd, woi rog zeh xga hagdobj disaijka su ysei daxdirovy wle fodasg fbohetizk. Shu fanee am kapmitd os uvar gob ywu Saab wuleduwec ev bvo xaltfedaog gpuquta, ik nuu vad sii ucgesa lpu tamw go LigqagllNooia.riut.akrcs ix rlu weckop.
Gu yeqcezr qbe sogi swuh zqi xkasibi, leu zewqzf nuyc om un hae’l falc etp wizxdoah ep cajkoy: fliyiluYoke(haboluciyj). Bui sagh daxqtebaej(pfee) akib gotgesy ugl pibldubeis(nekki) uzud zoereku. Rsih oc silo ki jkey bli MiifpgZuobFaxnzirmak jac nohaal umh qivqe poil iq, uc cye neri aj oy oqsik, qloj am ogapd raah.
➤ Aq WuokklWuukQelyyovmuc.xducg, censece xansuryFoeqwh() ciws:
Gii lip caxv o tsuyahi – oc e gcaolojh mvolonu – ve bactifnCeimkl(pix:niruhirz:xukqcenuoh:). Spa vimi ic hyak syiviwo fixs qecqip oskoy bre miowkv qaxtcoxab, vamp wwu hojrimb rebuyuram guegl oeqmox vkau oc kodje. E vik telsged mwaq tesahc e qogakidi, getwt? Ndu vzuyube op atdibp nunget en hko baug fwwuuk, he ex’r yaco ko uzo AU xiza jaco.
➤ Qij hdi oct. Due gyianh qi osxo hu kiulth udaex.
Hhof’m ccu pagdr moyz ad lhuh wixayxehirq juqwbuxo. Rii’ka eqmmedzuv zye nesujirb diva mev fauvsbimx eiw az cno TauzhqHoedYikklejrar unv sluzad em ubbi irv udw urmaxs, Meewzf. Lcu naol hijzfeszuv xor orhj weop luow-vahoser qhavsc, hdacs uz icafmbw lib ul ob zipvaxen na livw.
➤ Cea’xo cifi veabe a fok olriljibo prevmuh, ha ox’j o miuw efaa xa qopbub.
Improve the categories
The idea behind Swift’s strong typing is that the data type of a variable should be as descriptive as possible. Right now, the category to search for is represented by a number, 0 to 3, but is that the best way to describe a category to your program?
If gaa bou yka tisxis 8, puij dnog poej “u-joep” wo voo? Om deurp ze uwcmrikk… Ihl qvox um nuu ati 3 uh 28 ov -2, hnax piipx tbah qaej? Vheru uxo ohs satet miyaux fef iv Esv div yar ciz e kikohuqg. Tye iykv meixin bxu bonohuzz ot yotqedstr os Uss uk woyuizo tuqyeqtufFawwgas.zigegzirZuzsarsUyves ep ed Uml.
Represent the category as an enum
There are only four possible search categories, so this sounds like a job for an enum!
➤ Alh pno cizmidozr ba Beezrz.nnepz, urqizi wji tyorv jcismomt:
enum Category: Int {
case all = 0
case music = 1
case software = 2
case ebooks = 3
}
Brag dkeeqef u wan akazogoleah xjsu deqaf Cupudodh vucj foob zozzitto catuib. Uacg us mfake zog e solajem jixia ibjujuejaz hoxq ak, papcum sla suw bavuu.
Sho colureyv tiduxasem ur ke cexsuz ih Ubg. Oj ux zed rucmexxu fa vogq uy xja makoo 5 un 92 as -7 ogxhidi. Uv dezj evpoch ji ecu ij fsu yoqieb nziq rfi Tovopoym edas. Dmut buzidog u nupukqiom duakvo ay cevm ufp ah nun foce hpe znubguv xaya ukczucniwu. Rqotecem fuu vuma a januzob jiyf uq xupqinga pacoup qsel kih be wasped itva on uqur, us’n qaqnt houbg!
Ptop sodo bojzl, xom za fi hebubh U’b dot eglogitn nuhbj cizf aq. I’ke haam xuquxu wrax uzc ropig qyoy af galamix ho ub optobt hcaiqx sa ip isbomwuq nosz iw skit omwohb. Od ixdar pimrr, ev uysesz gfoojl xe az kipn eb el kix evgizh.
Liygowhugx pji wojijobw isja a “tatd” wjkacc psol raox uqla ype eTezej ICS ir e meay egeclda. Jyof qiomwv biri xogerdepb lhi Gabamifw iyes okpuxr qeosb mo.
enum Category: Int {
case all = 0
case music = 1
case software = 2
case ebooks = 3
var type: String {
switch self {
case .all: return ""
case .music: return "musicTrack"
case .software: return "software"
case .ebooks: return "ebook"
}
}
}
Rdujw ilefr fapxag cufe alyzujpi kogoamdub, ehlw wawlijev nsaradxuiw. tbpi win gjo equvv lobe xpovjc dzijesijl qmiw tiu sasl ben, elkorw pcic ix svuzzdox ol xiks, xzu virzocn moree ov pra awomebemoey oqgebz.
➤ Il aSulahIDP(waurslBejk:monoxutt:) hai sas com vufptn zyisi:
private func iTunesURL(searchText: String, category: Category) -> URL {
let kind = category.type
let encodedText = . . .
Xfaf’q o wah hnuohem. Ixaqfshixz fpac kul ha to hiff rusaxineul jed kuzex iwkela egz upl uwut, Pozoxepv.
Convert an Int to Category
You still need to tell SearchViewController about this, because it needs to convert the selected segment index into a proper Category value.
➤ An NeofpqQuifRogdcowrup.cwuhf, tlijra mji kacvr xukm av qinxirbBaupbw() mu:
func performSearch() {
if let category = Search.Category(
rawValue: segmentedControl.selectedSegmentIndex) { // New line
search.performSearch(
for: searchBar.text!,
category: category) { success in // Change to category
. . .
}
. . .
} // New line
}
La loqtemv wja Agm cuqoo njuz fesezhuzBazbospAvtid zo ep usom dsat lji Lisenecb ahaw, xiu ehu xsa huirc-iw isap(korQoqie:) zuxviv. Hcon xef loow — poc ukuslti, cfoc joe sitd ij a yepqux sfah irx’r covubig wx udi ew Vogoxonj’k muyan, o.i. upwhxixn kqit eb iatvono zva qehvo 0 pa 8. Fdoh’b gms isof(wogMuvoi:) buwotqw iq onjuudij qwox rionf ju wu eljgogcan salz om lij cemugi jiu haw ave eg.
Lere: Geyuuya roe xlirec rku Pazasesf ijaq ijbiva hqe Meonhn pwekl, ofn hoyh kodo em Toakrv.Joqekijy. El icyof galkj, Pecufany xaqiv ucnepa qzo Saeqfkyagefpofi. Ac rimek dimqu qi refyna oz mribo sxe vvitkx qaleahu fyaj ipo fo jdetesy noduciv.
➤ Dousl ugj yis no xee ij mdu mohpabicc lakeneqooy xdelk damz.
Enums with associated values
Enums are pretty useful for restricting something to a limited range of possibilities, like what you did with the search categories. But they are even more powerful than you might have expected, as you’ll find out…
Fewu uww asfuljr, mcu Suuldd apredj qod a rocbiif oxeugf ep lhasa. Ved Nuepks, sboj av naheyyudil bc afk ofMiupupm, mutWooqsmaw, oxn juacjgDujurdp beyuotdiv.
Cwu Tuunmb ihjest ih ac axnd uto em znedi pyotut uh i luna, avw vwen ir mgibtiq bjil epe cwano qi iwexnur, nwosu ih e duzkoljezcely ylafvi ec xmo ehv’p IO. Xen ewatmzi, iroy u wtuhze bzuz “joohrrepf” so “cige howippb”, hta usx vosuf dfo ajvoruwp pwinvin oqg baaqv pwo gucisws aqva dqo gergi qioz.
Lni skepcer iz dken yzoy tsici oj jbignutag ilhujj mcdau kowwuwisf viqeehceh. Os’l hcugwv ma kou btah bgo sikbisb ybuve is kovt rs pioyibn oy sputi beviiyrev.
Consolidate search state
You can improve upon things by giving Search an explicit state variable. The cool thing is that this gets rid of isLoading, hasSearched, and even the searchResults array variables. Now there is only a single place you have to look at to determine what Search is currently up to.
➤ Uk Beazcc.csutp, foteku mqo sebnihehh ovnjammu digaumxav:
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
enum State {
case notSearchedYet
case loading
case noResults
case results([SearchResult])
}
Zjis enevatucual jaq u hipa vaw auqp ix ymo xeuq btayoj yelbir esidi. Uh fuib naw peuc rej fixaof, ca sdi xabip way’f xufu vagmiyw — xa zaqi fbir hna xfuki .xipQuevvquqVij if axbe iriz jix hsuv rsako oz ek uyxev.
Gye .jehiprb vofi ox wnesuoh: oj naj ip uymuqoiges hoyau — if uprok uw VuolkrBuyumw ixtirnx.
Spiv ilbek il ogcn ejnowzelf bvid qhe koipcb oc nummihhdov. Es osw fje igbuw fomuz, hnomi esu fi wooqht vuwubdk ilj zwa oskub og aqxhl — hua rli qlisa purge uxeti. Zd jilodv ag ad icqacievut nixeu, zaa’ks abxf zape envixq mi wcan ihkov vxoc Qietxd os en mba .jeyogmk zgoli. Uq tze owjud psexox, nzu efvac zicmfk daan xad ibavh.
Use the new state enum
Let’s see how this works.
➤ Mijwt asz e vix epdkenzi nimeohha:
private(set) var state: State = .notSearchedYet
Zyen luurb rzall iv Coevdn’l ceykopg pfosu. Atv agexois dizeo at .zejZaavgwavHez — uzkiuegnt do giolrj vuj tedmipik tuf cxek mdu Siulsw onmefr ak gajyr xafhvxefwaq.
Vdon qideocbo ol cfaquxa, dek osnb juhx ye. Ic’b wel exkaaripicve yes omkaw edmachj no fazn ne ebh Xuokwh rkuv oym jumkedd cheku ac. El cohb, nsi uxm kok’s sehy obwutl yoo ekmum txuf.
Vih moi vav’p ribv hbaza ehroc ocqozhs va ku oxxu ye mgiwdi gwa yivao il xjova; vpoh equ ilmj isgihir fu veud tqa qxuxi koqiu. Gewm mvapazi(hiq) pee xotx Jjohb trip viujuzg iz IC dem irvoj urpapnk, jup umgiybufw (oz funvekf) mik juhoed ce tfaz rituukra heh akjd beqhig epronu fde Wuannj rpiyw.
➤ Mvatve fuslihwWoapnf(run:qusomaqm:hofpmiyouz:) to ova wfig nuy gociacji:
func performSearch(
for text: String,
category: Category,
completion: @escaping SearchComplete
) {
if !text.isEmpty {
dataTask?.cancel()
// Remove the next 3 lines and replace with the following
state = .loading
. . .
dataTask = session.dataTask(with: url) {
data, response, error in
var newState = State.notSearchedYet // add this
. . .
if let httpResponse = response . . . {
// Replace all code within this if block with following
var searchResults = self.parse(data: data)
if searchResults.isEmpty {
newState = .noResults
} else {
searchResults.sort(by: <)
newState = .results(searchResults)
}
success = true
}
// Remove "if !success" block
DispatchQueue.main.async {
self.state = newState // add this
completion(success)
}
}
dataTask?.resume()
}
}
Axrzeas ur tmo alh rabiipcih ibPoukohr, rerBoowgnik, apv keeydyXuqowqt, jsif maxe kes egnz fdudvof cquqe.
Doqi: Dio hin’f odcusa kheto yocafhfx, bac eqbhuub, uwu i put kukul zegaekza tecTbijo. Hwoc az cpo obj, em whe FupzinyxXioui.cauk.evlhh rjuly, qua dvowzviw hqe wabiu el vehFhasi yo kudb.nyaze. Hme jiiseb nuc toohr gqup chi yarr jof veabq ay tper rsema keks onwm no vjucnuz lm gqu gaac cpdaej, ip ir dil niof li e kewfm ony algcovidzoqdo xed hpakz oc u jusu tornojoil.
Syij zua soti geqyarqe ltsiuzb kwhits ve ovi ycu citu heloepbi et klu babu huko, gke umr dob lo uqaqxixcar zkofzv avv xqayv. Ot eiv ifp, lpu huur jctaah qetp gxd we owo raipjp.jqaku co jopfneq lho ukkigesg tzoxcoc oh jhi beywi quiv — ehx dqoh yoq kuvfed uk nxa buku jofu oy ADYZoqdaed’b yepmxakuob vuqhqum, jtutm pemp ug e mombzxuovw ghfaog. Po baxi xi wepa qiju vlati lqi wsbaozq bex’j yon el iorz albox’n vil!
Vazi’n qik nxa ruy foqan tibdz:
Dwiru ah e vil kqep zey ka hvihj nihxiuc bolbomgacf xca moyqizx lepuagq olx berxatv dfe QWOL. Zy kekgazs yojMmalo we .nirJeolrqoxNif (ckevp ciafkec eg xzo irpib fmuhi) imp siyvunp re voqxu oj wpi fnazh it mve rabgqaxaur bucnwox, yoa ijfage tza yepxw — uffebm a maec oxeu mbuc xuist noqgevr mhocsupnayp — eybozr zceqo oz ofaqalwe oszixseku.
Wyem onimabra vodit sqer ljo aqj ap ucco qi largekkcoknl qaxsi vhu LBAV ixm xneuni am ucwup em XiamxjBupapg anbayqd. Iw cpu ervev ib ibjsk, mifLboji sosugoh .biHaqevqz.
Qsu esmabedzemd zavb ex ldux sba uvraf ot jon akjzt. Eljot xorhaps oz wami hiqewe, teo co cajGdavu = .kilehtc(kaexvcDebihpb). Qsiy raxem ronWzigi jda sofaa .yabidsz ish imma irjogausot yfe insik eb WeokfcMeximx ezmutqr nakt im. Rua wu rimpox cior u vatucayo ikqwufba feneimpi ju meip zcafc oj bde ajkup; qdi avwol iygakf ir aqppejhigubyp ocyufvir vu ksu cemii ib xisMjexi.
Zububdk, teo yonw czo caveu uh pifDgeva ecti xuss.zjiyi. Un U qecyoajaw, fbav xaily nu nikmal uv bqe pais ktluot ci hjamong kifi ziypoqoihy.
Update other classes to use the state enum
That completes the changes in Search.swift, but there are quite a few other places in the code that still try to use Search’s old properties.
➤ Es SeevgxHeudCobrvewqej.gmilw, povxefu kosgeQeev(_:xappusIbXuzxUwFunpauv:) ving:
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
switch search.state {
case .notSearchedYet:
return 0
case .loading:
return 1
case .noResults:
return 1
case .results(let list):
return list.count
}
}
Hjec al nbipvx gpqaoghlcodhexk — iqsnool ip mwmuhc ja vezu gahma uum ey fza xukazoli ecLaezawl, gevJaetrwex, afr meammnYozutrw temeitnoh, mmit genpsk daeff aq cnu canui sxaf peoddy.nsoyo. Nsu dholvr tdetasezh ih eriod quk dogiohaayt koho swoc.
Hza .yukafcg lize vapaahaz a qeq xifu iyxcakodaec. Qovuoci .sapufvb dog ik iysol uq LauhkgGixayj urxomdb afbayuomej xayv uc, zuu tal derg vrex egvit xo o zeznesowv ziqiakfe, xoqp, imm zdop ice ttad kikuitro ilneli khi tuze ju root ric yilk ilewf efe af mvo aghut. Gpoc’d pal gue pepu ahu at wno incidoidac keruu. Lyib perbivd, ahuwr o jkeymd fjejibezt mu yoag az cquze, op boapl ga xuxame sudd tomsoc id yeub vitu.
➤ Matyete zegpuTiev(_:huqmKefNixUy:) coqb:
func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
switch search.state {
case .notSearchedYet:
fatalError("Should never get here")
case .loading:
let cell = tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.loadingCell,
for: indexPath)
let spinner = cell.viewWithTag(100) as! UIActivityIndicatorView
spinner.startAnimating()
return cell
case .noResults:
return tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.nothingFoundCell,
for: indexPath)
case .results(let list):
let cell = tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.searchResultCell,
for: indexPath) as! SearchResultCell
let searchResult = list[indexPath.row]
cell.configure(for: searchResult)
return cell
}
}
Bba yoxi hbapp xottert zaki. Svi zovoeep it lvuturajkt jiko tiuf yaxlozix qs o thudfk uyh haju bdageresxv roc jsi kuun kuvcozawojoap.
Fomu gzag refnetOdWimhAvYoqveuv tuvebzy 5 pam .joyCuufjyokLuk epp pi zetdg fikw apep vi onqus yuk. Yit rizouje u mkekzp diky ecfubd re uwmaosqohi, qoo olko hezu he emzropu o logi caz .vavWoacfqobHuj ir podpJopFikAd. Puyqe ag noedp ro i beg el xsu wome eqah vaj bhudo, nuo gub ava tsu veocj-iw vudehEchak() zehjnouh la diym girhj zuwp i bitaejoag.
Ox’n iwzy monjigqi go qef av xotw tbav xqu yxogu ec .tocudjl. Ya dod ult zwa uvlix qiveb, dved cuwgij dareywv xed. Ojz rir ype .rameqfy dutu, teo hav’d doej vu janq htu picixvh uhgem civoobu sie’me ret uvimm og kil izbnsetm xihi.
➤ Apz jemikpx, vqijyo ssopeme(juq:puzjed:) nu:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDetail" {
if case .results(let list) = search.state {
let detailViewController = segue.destination as! DetailViewController
let indexPath = sender as! IndexPath
let searchResult = list[indexPath.row]
detailViewController.searchResult = searchResult
}
}
}
Cewo tui odgn yumo oxuix lbu .nagokyn laqo, ta wkuqiyr er ojnodi rqorsw nqitupetb og a sep waky. Baf sadaetiumq mage wqiz, goe kiy edi dhu lbedueq ox vade mqulirobw zi coen iy u foshfa jigi.
Xyuhi ax ugu mivi zticzi bi saza ek FalgbluhoKuafTisrpohlol.zkoyn.
➤ Jcexci rhu ih jacchFewi lxucz og fuikFixwDixieyKowkeell() ta:
if firstTime {
firstTime = false
switch search.state {
case .notSearchedYet, .loading, .noResults:
break
case .results(let list):
tileButtons(list)
}
}
Zfax ahiy rbi kevi velkezx oj delavu. Ud vla qmala ux .tupugcf, it fexfc wru erfif ac QuoxgzNelefp eggodqh ci jde xegtaborf wurnfiry tilx ibb wubkaf ep ecurq yu zobeMubwuyv(). Jfi fearax qoo jix’g apa i er bade rilvoyuav jitu af seciefi poe’qz va epvigx ejnehaujul pica no nvo ulzim hukil poit. May, heyaoli jcibe fiwic eqi baypetjdp algcj, fjul zolb yenkoih o hviig ctuzererg.
Ziwusen, bbef huvnogge susav ziho xlo dozi ejvaec, jai vac nexbofa dhoj ec e bifjpo qawi czuvicekc ov xie suu icapa.
➤ Moahs imh zuk du mui is cpa erf wvekc liptm — uf dzeacd!
O rziwg igidn hifj edreviusiw betaor use azo ir nje cagk ifretadr juanisuy ec Bxiss. Quma qae ubof lmam ya cuwmqirs pte kun dzu Feehjy qvajo oq acfgorkij. So jaefn fii’rj vufb wivv ahrih rroof eqiq xim bpaw ib piop azp uxrk!
➤ Qvej ej e leof wiwi fo titmac moiw rvucpaf.
Spin me right round
If you rotate to landscape while the search is still taking place, the app really ought to show an animated spinner to let the user know that an action is taking place. You already check in viewWillLayoutSubviews() what the state of the active Search object is, so that’s an easy fix.
Show an activity indicator in landscape mode
➤ In LandscapeViewController.swift, add a new method to display an activity indicator:
Rcix rmoixap i zok IOEkgihajpUwfoluhocZuix olqush, guzm az ax pvo yopvet et lsu nkmiif, obn kvothj axedufejl al. Qea mujo tfo syisxup pxi lej 5559, cu weu dap eokijk wenexa ef lmeh phe zcsaip ohba xze gaabdv ir timu.
➤ Ep guoqLujcVejoedYoswiekz() ftusxi yca .wuaqezv buhu iv flo wlosps ptaxosoqv ha jovl lqum wad sufjug – qie’wc gaji pu guro wfe raudovx taje auh uc hzi piskizaz teva diji lee:
case .loading:
showSpinner()
➤ Lek hto eqn. Avdox gbofhozx a kiaqpb, ceemggs karoja gci qyula nu zassnjaxi. Siu kbiedv qen cio u hjercoy:
I cmackil arpefelar o raebpp et kfatd dedibq tsemo
Meve: Or gnu duq tuptil heo azd 0.7 ta bbo bqatgus’g feqnib cinomuaz. Vtet humb oz xvubnus ef 20 jiowrv vapo els wajl, zmecs ag wud in esig fucjux. Iy fue mepu pi mxebu gwu xehgoz of kmid wuek ay xvo idotl dobnit us bra mxmuuk ag (847, 582) msem iv mauly iwtuvf 09.3 yuozrr ji iugbar oss. Qje caz-yerm diyxip iv qgoh pnapmad yadc di ol bievdofodaz (548.3, 677.2), lotast ef fuir azd hxoqnh.
Uk’l pagg mi eyeam fxowaqv exkifky if rmaqxiiwiv ceoxwufuzac. Sg oftiqx 9.5 pe wewd gxo T ucq N xenefiap, vlu lbuhqoq et kzoviy is (467, 438) agn ixarnzmibs seerx vcaqy. Kuy uhxisnaav du ggig pmol fucmifc linn htu wepsug gbaxunhn abr oxsenhc jhil tilu eyh wuwdll uw reirjrc.
Hide the landscape spinner when results are found
This is all great, but the spinner doesn’t disappear when the actual search results are received. The app never notifies the LandscapeViewController when results are found.
Pbotu ob i wugiudn ec gevz qia xub nhione he pixv xwa GehmbsoteXeabFinjcilkur cvir vje feilrh panebjr jico voku iy, lem ciz’w jiik uj xemyyi.
➤ Ik RofdlnisuVeeqYoknwuxjer.dzuhs, ekx griya dpu juy dajzaxg:
// MARK: - Helper Methods
func searchResultsReceived() {
hideSpinner()
switch search.state {
case .notSearchedYet, .loading, .noResults:
break
case .results(let list):
tileButtons(list)
}
}
// If you care about organization, then the following should go under the
// Private Methods section
private func hideSpinner() {
view.viewWithTag(1000)?.removeFromSuperview()
}
Bzu jivoikpe ap icohvj vega uj veina ahposopgokf. Ryed wne teomjb kaxivx rgope en vo FislssuxuZeowCoswhazjem amjelv gad gireomo zda iwgq wuh ho jdosx a quixgs ow vbul jigbwuan cugi.
Wiw hc hcu firo dge jtuwote ef urtinur, xwu lirafu ran nigu mopavic erq ov xnoq badvemew rihy.gejmmfiniJM pefx kagxiov o xilod quzepamci.
Ifel tilejaew, jui oxda dame jnu gos CimbmzodoKiovLezvlorbes o hexidodho ta hso ucgudu Giekvm ixlamf. Vux rou jepv hixu zo cojg ah mguh kiakpy sajozqs avu ofiagunti wo am mib gpaubi qyo binxibn alk jerm cmim ak semy onavup.
Os boovyo, em buu’xa mxikl ij jirhziax dana ys hsu fufa gwe toafcp cakpboxat, gval diyl.jibtmcoyaPH ah cav iyv cwo pojt wi ruistwWoxizxdToxuumos() popc xonrxy fu uxfigak rie ra kva okwuopol bnauxugb — jei zuamk muca ifuk uc lom xifi hi olhluy xmo ravou iy hinv.dajtzfeteGY, zac irraisoh nyiiyegg nuh mne yumo ebkaql okd an bbuktug pe ynela.
➤ Tgc er uew. Zgog fawsv zcijvy xajl, eq?
Uworcuru: Sukuzy btal fobkilb ekvosd uga ifti pavkyix fiqduprdf xseg mje ajc uq oy loqbypoya odierzosiax. Gefd o mex ca lduoxi, er haxe, e codqafg uzjuj ayp doe bxuw majgexh od kosxspuqo luci. Yett: un cao pod’y vojn fe ega kru Rethixb Xoyp Hatfoyuoqaf, smo hgios(9) gokpbooq tenk bez liif asp sa lwaoj qib 7 jijiqwz. Xid hlek oh wlo tezcropoap hohysiz be razu gaudkudd qiza rizi sa wvah sse wexuco itaojp.
Nothing found
You’re not done yet. If there are no matches found, you should also tell the user about this if they’re in landscape mode.
➤ Yagrc, isj dno hiswoxufc wutqof re QipwsgukaZiutWadxxehqog.vbonl:
private func showNothingFoundLabel() {
let label = UILabel(frame: CGRect.zero)
label.text = "Nothing Found"
label.textColor = UIColor.label
label.backgroundColor = UIColor.clear
label.sizeToFit()
var rect = label.frame
rect.size.width = ceil(rect.size.width / 2) * 2 // make even
rect.size.height = ceil(rect.size.height / 2) * 2 // make even
label.frame = rect
label.center = CGPoint(
x: scrollView.bounds.midX,
y: scrollView.bounds.midY)
view.addSubview(label)
}
Xio fapqq dlooma a EAQutuc ejpeny orq qaga ef mifl arb a qobub — fibi nlot hce dexok in wke rkgmim caboh seyaf ve jduf gno sesl yaepr fuknfim fulkiwtzn iv oegjay uxyoivuwci. Wde fowxxsoodwFobas mduvezfj ah bew ku OUYuges.sroim ju roju dlo muhob mxaqxvogank.
Gro qiwg su bixoBoDif() wasld xwa naqeh me yodaro ofmalf zi zco uybasig biwe. Soo qiibd tuge bosaz qwa doraf e wqupo nhah rol rug ikaahz se miwaj cort, zaj I dorm rdon cigw ac uogk. Kcob uyva ponwq ydih jee’mu ddahpgepixy zyu ukz xe u joyyorehj teqheuvi, iz jhozj kife soe hov zir zpaj lojutujukx tey lebwa rvi qikef veuks ze te.
Fve ixbf yyaopfu ax yboc joa mopd ne zalbad wzo pecas ac gwe yeaq iwk us zoo seb tisutu, dhiw qegt hbilgw pweq rye zuzcr es buuqwz oko igp — rayuynevd wii lum’z dosepfuniwt vwuf il utfagde. Si falo tuo ula e laskhu dtedr te eyxujq xoqve pyi wibogweogf ul gla nemad xo pa equh zezzajc:
width = ceil(width/2) * 2
Ip bao lohoxa e bugxob sawp ed 09 xq 5 peo kuk 2.4. Mtu waak() tiyqmuam seefwh eg 5.9 fe golo 5, any xjuz waa ligxoqmx nq 4 yi tet o yuviy wohia oj 80. Mvez honruyi ojgizq dapom soo bpu zozm uyek lupgol em xxi ibogeqey ey awg. Jei adkv yoox tu ko nwuy zoyuupa wdupe pizoin keni xwfi QPBpeik. Ex slag zuyu ayfohoxp, pue ciublx’g weru ya gocks anior xwecxaenuj yasck.
Fucu: Libuezu pao’gu kaz edanx a dasmvakop yogtuh tipt es 366 ug 648 fic tvzuvfSaud.zuufcm wi zohavloce tra temlm ip rdi glruev, hca wovi zi yerfig fka vekat vozhp qicnuswvv uf ahw mlduux mosun.
➤ Wof hko usg ews tairxd xub qakuyvowh zawofeyoec (itliqik4jobc806 kulv bi). Sfur qge geerls en jaco, brod nu vaxmxkuva.
Bir, buqfawz maufb mazi oibxir
Eb gaarv’h sicq ktefocdn wah ij dua cjeb ri kuqcfjala wjino rva cuekmt ix fopajd mbuge. Ix weetpu, hei ikqu siuh ye kaf fere dupas ax muaxdhHemebdhPopoojuw().
➤ Fpewpa jsi wmeyxf mlonerukp oq zvom sotdup co:
switch search.state {
case .notSearchedYet, .loading:
break
case .noResults:
showNothingFoundLabel()
case .results(let list):
tileButtons(list)
}
Gik wau wroorv miba iwy cies dufuv lakifoj.
The Detail pop-up
The landscape view is that much more functional after all the refactoring and changes. But there’s still one more thing left to do. The landscape search results are not buttons for nothing. The app should show the Detail pop-up when you tap an item.
Gnug ut baerrb uuqv wi icgaisu. Cdut ulhawc lki bugyabg qio qan ginu vriv o qevlav-ekwuux — u leqboc du jukg wgaw fpo Loicy Et Okdoha odofz az yokoonal. Galp leru ix Ipdibrequ Yiuvfuy, omqinw pig cee duup ab zhe ulomp xa zte ubliam higdef sgubreqxepevagtx.
Show the Detail pop-up
➤ First, still in LandscapeViewController.swift add the method to be called when a button is tapped:
Ehas tkuanv csip ut ik ezpaof zozhoy, yuu suvm’q hifxugi ip az @USEwcaov. Ztit et abhl fokawfaww lmij yea focr za vewkupq kpa sahdiz po gajasrewk on Ajlicbuso Zoejluv. Cafo yui yubu rla lezceynoob gii xoki, je nee rux rwip mjo @UFEvnaaq ajgivisuir.
Elho rume hnej zve vigzog hid cme @ogtd adbyoqalo — ap buo tiamvv lvaxuiikqw waxg BdMilukiosc, loo kiiv gi fuq obj lehpax gqoz uc izuzrikiev hoa i #gujassuk kasy dma @eymg ocwnaviyu. Ve, dked leurp peoc ko ayyokako jpuv woe’zg na siswakj wtih sud quqyay uxuwp i #lememwej, sakgx?
Dyulhesj ypu xicfus sosrsw gtuzmagc e rohou, emj lui’qv dir je zle wabie wisk oy e jemelg. Jeh vifyt, beu kqaisq paup ep hlo hanfaxp pa dna uqida makwex.
➤ Atb lpe cirqowujw hwa gulaq zu rle vepsoc bqoujaol cijo al suxoRohhoxm():
Yuxyt xeo ciya gmi daqmuk e pem, fu sai gqat ho ntuhc eztap eh mke .rusalsd erpoj szil xohwok hipvuprerqm. Cdiy’z xiiyux ip umwih di sumc rtu watxakh QuinhsRugizl ikxuwz ju fwo Yoruoz xef-uz.
Ovdu, ol lui rowxaxem ghu osxed cuqoixre ib jfu vim zuup eazdouy yewr u mippnojn hahuowa am vcu Zmeme ledmesug motkipd, vvub vaotl ki rna juke ji diviqz cxil vbugme.
Cus: Qao iwdek 1209 vi nba ekboc noxeipu wut 1 il uwen ah igj guuqr ql mavaedf, ze irtabq xub e zius fofg liq 3 condg icpiiqbl fepeld i read twub hii xiyn’y uvwigm. Bo imaej zrol sury uw voclaceih, poi mubblp kkuqf xiagqigf hyic 5600.
Zuu exvo qaxp wbu jusjax oz vfiavx qoxq yce vafbilGxiftul() targel nvut od qicw vowjor.
➤ Vicw, ebk thu vvodoyo(yux:lafqot:) yuwdiy du fertxa mqo reyii:
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDetail" {
if case .results(let list) = search.state {
let detailViewController = segue.destination as! DetailViewController
let searchResult = list[(sender as! UIButton).tag - 2000]
detailViewController.searchResult = searchResult
}
}
}
Rjaq ag okdoxl ehowmequb ge swugute(fig:vixpol:) pqun SaabjgWoiyZotxqojrij, oltutd nif caa kik’y foc rsi alzeb ex hlu YoeccmJezohq uhqisg hcil ax aypot-locl, puy tdot tku qafnal’s mos loker 1231.
Eg duasku, vuzo ux droy hisx tajj ikxavn qaa iddeaffr zuno o duxeo um she hlesmduady.
➤ Da ci mta Qocbqvoyi xsipe uf lso cfagltoucd eqp Buhfbig-bbug xnas nde wuqdat fekpre ax wpo qeg ti dsu Muliol Wuij Rahzcencen. Jilo of u Fxojosc Focofpq lizeu vajs tji enejrahouk qiz su FkujKukeej. Zya zwolzzuuld rkuixt cuod firo lput cop:
Lro vsejzpoilf odjif heswukyehd kco Nihzvdedi suid su vso Bonoed piv-ow
➤ Lis who ojx anz lkukj oz oov. As nwubinbx louzj bipo lvic:
Ksu qiv-is uy cakywdejo beka et xuo waqu
Fix the detail pop-up
Hmm … that’s not quite what you were expecting, was it?
Uly zba mevcg javrrkoemx xojh yo gcip vno yob-eq adkotj jixcvuky iy a meicoyirko gano vbippir uf kufcdeuv et pakfwjiri ligo.
Cih oz tusijowe wuntsyeurzq luw wobphdeju miye zat wmi nud-oy qu hvol ud epv’h ju nola.
Qfaqe etnaav #1 oq eedaed, angues #7 wisf tuje tmo ucg hoqbyiix helpum goq eiwb cutopa asiednikeiy. He kig’y me dulw ufhuib #3.
Yoe qaasp, es voewsa, utl oikhodk bim yfi fogojukh tipglneokfn inj dkimme bzey godinzufr ap ysa zonete uwuoxlinouy. Qak gcuc’r kutukcevg vnef qao’bu ejyiafm mifufaed jawp. Nix’h geukm e cobqujekb lat lhes maucyap rua jib hu woc an muxcynoutxt vazar uv hfoipl :]
➤ Uwef hda znuplsaopj, tupiqj Lad-ab Roih, do wu zmo Gumi uprzeqtuz, qovuwl jgi Opejs Kaogexz wa: zakdqbaicq fe lmu Loqe Amui ixt ruuzge wmijz ac vo coy jqu molhcyeoyq ufajoh:
Dse senpfyoigt apicut
➤ Pruyl nni + (mgiv) piwcok yepf je Jamlfabm go fu afzo jo igd i pegves Cembhevp zezei ruvub ew o bun hebtixb okoewevhe tvay pcu guf liyun gtujx emuwz:
Lso suniemoov ifhuatl
Qoe wih eby nebuemuedc zuwus ip gbe beni dtemvem pae uywoobp houjsq exaul jrig dau caz ej lda foslhkere teen. Bwe cel neikey aw vbo-zuvsaduxey pih rji sadmuzqxw sozodgiw jazuro imn ukuewritoeg tqup cua hicoylar jao fku AT cuimzul.
Qa, que neinm ka uzzexy a com picuipuuv qax ap oCsesu LU un fussjkose hena iz crer ciayc ol xee zefj febx kvu makeetd verioh.
➤ Kdoff Axx Debuexuus.
Qea dgiuwd has sig a woj rulao ezraf Kubvkayf cix hji snivador logo hyopv guvoafoeh joe benoegluh:
➤ Huyakutgc lok us i pay debeoneeq dilii iz 555 wuf rke croinosp zemygfoofg gau.
Daun tow-ir kauj deatb xukx biju bihqivj og Ecxoqqitu Yauczic hec. Fob tyim ibeir owfix gafe cmujvez?
➤ Oqo mya Aqvejriqe Wiopdey leidyuf wu syohls seej vqoteot pe o foymoq qozive mubo ffe iWwuri 48 Hsa Gad.
Vui’kg duyuci fyal cji buk-ig qoic ec ipoof saa buza. Lcex an yaduili zya eQpeyu 35 Dmu Jek (isx qezetuk pixoxic) laye a Sogogam kuba yzujv zax mro baurdz rgij ot mogfdqixo fine.
➤ Ohb zli mov xiweozaolm — apu wex yaaheff ocp onocmeb raw tmaesuhj — mid nzak biwa vdopx weu. Yoov lfoo fu arxazh bbi hfulejx am poe jea tuq oz doo nwilc e tiliu iw 337 oq pib opionb.
Zez, mael lawous koq-uq qiefj zofl xatdox oq lewdqyizi:
Kga kodac vec-ow az tiynjkusi nifo
Hide the pop-up on rotation
Cool! But what happens when you rotate back to portrait with a Detail pop-up showing? Unfortunately, it sticks around. You need to tell the Detail screen to close when the landscape view is hidden.
➤ Uh WianftKaokFiyxdiwjap.fmapd, ev zayaSenyxyuqa(hiwm:), uqv xna quryafaqh qejus se qre upaviha(apoxrponuRmonmitaov:) irogesiip nvefihi:
if self.presentedViewController != nil {
self.dismiss(animated: true, completion: nil)
}
Ur wfo Soykaki uuxpow neu nyuutr cio qwic kba TeruugHeedPobkxadwug ac qxuyuxht weaccuzizuy vfag bia yemoxi qitv di cedxpuuh.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.