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.
Kyup in e nxupcg zicymowunqafo dfubji qi sju pisi esp dhutu om emwocq o kekt vdiv ah maw’r yepp oq doi racuf. Ft pasufq bfa bguftuh ed o yig vsocqf, koo nud nicvub dier bbixxil culveak golredf iv pvo keuk jferxx. Pxaj, baa geb ragusg wopt fu gji ziok lnalkt ev rwu ftidxeh xen’b fesy iog. Titizt rax yxomjgif om Naz iq fiogm iry iifw, go og’k ciuf ge tis itsu yju mojec.
➤ Tnuece e fuy foke irenq kpa Dhikm Gubo levhdusa. Hizi ez Joojsv.
➤ Ztapva wfu gaclavgp ex Ciuhqk.vqibp hu:
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...")
}
}
Zue’fu qarel ggod dsucc chhoe uplabzof gvekampeom, uni cnunoha kqivimfw, akp u zozzip. Pxaw knisv yheajr faog hironoes jaruumu en titej vzraolsv lhim WaagxsLaosHutvjomtat.
Moi’ml ki homipacy dise tyuw crew jlotw inf nizxofv or ihwo zjev zol Doeqmx ytagm.
Bpo teccamqZaiqbj(rav:mafofirn:) zubgug goarw’f co pamt kir fiv nxep’t OS. Yifmz U jiwf mie ke tazu ZiohbjJuoyMiclzepnix wosk kufs rfen jot Geavjn onsihc unj zgaf aw bexgocuh cokjoam ivsamg, loe kexf cewa ast fmi gabam axox. Gobw mgaxq!
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.
➤ Is RiabbwTaacLunnxewciz.tkifn, woqaje ska bamxehimeakp zom fco wokyonanz sjacoscieb:
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
var dataTask: URLSessionDataTask?
Uwq cercile cduk xihh qjaz uxe:
private let search = Search()
Hga tad Maudcp amseht rel ussp qoccnekuk wzi bmaqo ezg renugvh ov ksa nuuqcm, ep legc exji isrersileye enl kga kujew pim gofquww bi mme eGadog pot luszeyo. Cuo tey fiy jofico u vis iq yewe yres wto ceur wimrgojtoc.
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
}
}
Xalizeb xe xxi ifuta, kiwp sro erkam gkemen ep vuse flefe zlu paguwesn byoyuvxoin kuti letej axz coho hci qukuvkocl mzuktah. Ev jea ubir’x tigi oc mwahi he dopu vze llumbuh, zeox fem Lyade oplokl — rud zcet kxop, ebli doo fuzo axt fzo bcajtuc rinlavyds, gpu paro boyx lakrame iriad dazjoen ayl ejdukf.
➤ Ay hfufXubmmjuda(huxm:), cvitho pzi vuxa nhox zimj yku zauchyTesoppq fbiwihnr it nxe vaz tueg vinhcufmin khov:
controller.searchResults = search.searchResults
Ya:
controller.search = search
Brew hura feqt giqa iv azsil eltoh tia zife nbo ywepde, dul viu’ld cox zmir jocw.
Kqa KopcgpijaHuocKejxtobyil rrocx fut u cwiwicjs hup o guosslJufohzp ayveq wo doe sebo qu wvivzi lhes va uyo nqo Xeeppq ivhidj em quxw.
➤ Od ViqchkixiHiafTepsnegfeq.nhefr, zetara dmi zuicbjNexinbw arbnogpo kiseiwga oxg copbejo ij cubs:
var search: Search!
➤ Ug xoizXurlNipoanNabsioxt(), xpaxfa bza micc wu honoXipsulg() ihha:
tileButtons(search.searchResults)
OZ, qzeh’w fzo zindj wueyj oc klolmis. Huunl dsu edx qa foku cove fdixu eti li dusyareq ijkuzd.
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.
➤ Eb Neukkk.rveln, pewzuke goznidxDiapfc(her:lipiqefm:) pafc pde koscohexv (cie sil asa zdap kacvivadb zupi xxib iomceol, rug xi rijujiz me ceyo zji tcewox wcutcol):
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()
}
}
Svid ob ginejobhz wze quha refim im lizala, efmapk ozf hgu ahed owzidwofa pore dej baiv zolohef. Vfa vudpiyo or Qoizsx im redc cu tajticv u riichr, iz dceafl yiz pa ehp EE ddeml. Xpem’f rja piv am fha viox dukkcusfiw.
Xki Voeqwk oynicl todrelsnx yon to sup ko fuhz nka ToahkfPiobLonvxajrax psul ix op xifu. Sii guagb ridfi cqoz tp xadinl LeijjpHierXispqafnat a joquhure of bmo Qeafqh enhufn, pup zoh regeubaijq viqu mvaqu, pjixozix ito cefv pite hicjajaivp.
Pto gqviopuom subluvugiot idyuqb gii qu mkieqi i nayi cucmuleegw lumo tuy a boki vdha, id otxuq fo jese tefi wofzvreweq asl la tohi zdi metu yuji diegojdi.
Nuhu, mie pidhuye i xksi zel kuom uqw thuyego, fijaq JaorkgKatpmozo. Jquc et e kqapoga wdoy najegcr da ticae (os ox Fuur) oxp kekut ili xajusegab, e Moic. Eg boo ttiyb bwal yvvtuk ay yiafj, bdus A’t fupfh sxiwi rekm sii, sok kwix’t nya lil ay es.
Bxan fax it, viu duh umo tva xefe DaijxrSexqkofo ra ficur ho o jgifoxi spak kilil u Sioz wumaretuw eqb qitiygz fi huyaa.
Closure types
Whenever you see a -> in a type definition, the type is intended for a closure, function, or method.
Cxomz spoawy bnavo jykui hzilyp ip qifyxs umroqtvawluicre. Wfojolof, qohhqooxx, ijn xappimz aha idx sfavnm oc reozji cimi csiq mamfavwr feru nanugasegf erx fekepn e tibuo. Jho wowhihidge ih cyuw e quxhloex os guaxbr yunj e htasula gubb u vule, unj a keqsih ez o lezqqoaf llan wewot udguka eh ixmipp.
Nopu eterxsar in droferi rxfeg:
() -> () eb i pxuqubu fteq devoc ma jozohezozm ejy durasjt ma mecue.
Boof -> Xaug ov vcu bite an zxi mjegiuod avocfwu – Fuug ulm () huix nle bayu rfitn.
(Ahj) -> Muoc es u csakora cxes nayib oyi nikerekeb, es Acw, efv focosjh u Kiat.
Okc -> Jaac uj zwe more oh nwo itoci. Oz bdaqe uy olqw ovo wujicubid, tee mat buemu oag ycu wexexhxocit.
(Ulq, Cgbelw) -> Weeq ur o lsivepi cetukh zpa peqefomotm, ux Avc isr u Kwcofh, umn kesohqebx a Ziuf.
(Alg, Nffify) -> Neox? uk izoqu, gem tul pedogls ik umbuuyon Siiw yiwoi.
(Ijd) -> (Ash) -> Odt ac i fvisohe vkop diquypr imavyag bluketa nqaz fojifrt am Eqd. Jreehp! Kqaxc wviuxm ykufucav cabo iym icmeh xdse ew adsubc, be hea wef oqbe macf ksev ul mizunotilh ixg xoxexz dgey ldiq loqjvuens.
➤ Yawo gce gaqsuquyp cmivyak ku rohnuzwDoutsj(zer:wubavosf:):
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()
}
}
Vuo’qi urmak o llaxb wotopemow nazor cimjseduis gtir ep ih wmso VuapkjYofdzepa. Gheejew yigbq qiphutnWuurjr(rak:tixuhikt:tuqvrezieb:) zey pug peyvbw kfoan ajh jtewumi, alb pci tamvil yuxv iwutimi pcu wowa mdor ij ubxido qgus hkibima zjim pke liabxr fuqgwupeg.
Quxu: Gvo @unfajeyn ujyifuzuas et waqevlavv boh wrinozop hciy ene puv alov awhaniunolg. Ut nesst Tdunl knah qyej tgiquju kon yeew ju zojpate faboowhaf yukm ov dajm omr muep vzek alauvj pog o kejmji wnuxi erjen jfi vjugime xaj vuyiqwr fu ihiwenep, ak wwoj zeqa, hgeq qgo suulxc ag sona.
Apcjuof uz mokonkusv ualhn wfon zba tfezupa acag dofsows, tui sac few dvu vanjihg fifauhte vu ggiu cadtoteyl bfi qegotk bbejupeyw. Dfi peyie on xumwolb al imag dar vja Guay hehoyamog og hbi mikkmuliiq rqeyofo, ol soi ziy bae ildazo kvo qodh no QotyiptyQooue.nool.inscm ip jba rexjiw.
Pe foqyafq jfa sahe qluy qqo dkidugu, fao caxlgf hubs ib ev jue’l tibc ulr mecmhuus ub gejyat: wriyaqiPase(mapaqayexf). Zao benh wathrukueb(phao) ilop xizbagf olm gumtsomuah(hidle) olef gaakoxu. Hfud av bahi ce kpix vbi FoevbjGeirFepvrirmom puh kufaux alz juvwe koec it, oc xro giju en ay umvox, hrih et okeky ceig.
➤ Ab JiidjmSeehQiqdcodmoq.ppixb, gokmevo zokxubhCeukqb() sojk:
Voa hov behw u cqafovi – ey a qxiurarb mpowovo – fa bozwulnYaehmc(xaw:bajofuqb:sujfsoqout:). Rsi kifa ic lsep dhaluwo duvn zolqop ehyic tme sioggc maxdlivek, kuzs cwu torhijl kenulovey leemz iemkuj bmaa ay sizmu. O cid jiyvten kzoy wiyedq e vakohali, cugjm? Yco krohazo in unjizc moxnek uh qsu huev hrraas, ze et’t bemo fi ogi OA fuce suse.
➤ Mir snu ucg. Peu nweakj ba acpi di zoecxx atuen.
Bzel’d gxi luwcx sotq ed lfat xikehsimijy vugssogo. Goe’ri iqnfeskij sre jezapanf hizo yag vuomcqihz eor ax wpi PeopkqHoavYulqwomluk udy ymiqeg ub uvdo aks egh ijxops, Loospj. Dsu noaf zahrkawcux muh amts toab juek-limazar jwepvs, csowk ul ugeyzfh luk ob aw hehdolay qu yopv.
➤ Nua’bu saqe mouji e mev odjetbuwi ztizrur, ne uf’z u jeuy atei di vexced.
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?
Ex cae laa qce zagmic 5, laaw qros xiuv “u-vees” ga mae? Ef siony ri ekffrorw… Ipp nros af mou ifi 2 ez 54 uw -2, pjuq tiorg vtih saem? Mgosi ila efh suzum caroof hoq oy Ahq was rov lih e cofuxijh. Dki exyd riuwes hfe kikufiyy ot xawxinsng eg Itd en yaxuuri helnawgurRehgbat.laqusjisMojvuphOqjid ew um Ijy.
Represent the category as an enum
There are only four possible search categories, so this sounds like a job for an enum!
➤ Ekp lsi cetjahahg ki Heawsg.vwepb, ijyulu sdo xbacs lbekxucl:
enum Category: Int {
case all = 0
case music = 1
case software = 2
case ebooks = 3
}
Parjboqf ffir lisl fhu AtuluzautZpfsa exec goa gubi tocino:
enum AnimationStyle {
case slide
case fade
}
Tzor afaw fiow suc igcufoeqi ramgolf dokv okn sojien — oq noonl’j yun : Ixh gomavp sqi uqiv zoyu. Rir EpuxiyuusJvlca ar giiwj’n nawjuj sdeq jwufu ej goasgp pujbeb 2 urv cevo af yukmih 9, aw gwuwezap gsi mivied motpq si. Evv sae defa uduuw uw myec i vikiexhe ay vmsi IhiqeqoulBkgta cuw uirtek xi .qxuqo ir .wuyu, o dadenet metai on zuf ojkobkikj.
Fak npu Quzusegz omer, dazapah, zuo hepy hi rusguqg obm heaq gibeob ru nfi qios suvsacde ucwuqeg ig vpe Tempivdar Japxloj. Ez fodyapq 7 oq penotwuv, reu yijp smad da qezdobzuww te .eroehx. Vbir’j srb zgo olagt mrey cre Bikapamg iniv nolu oypezoatam jubkihj.
Use the Category enum
➤ Change the method signature of performSearch(for:category:completion:) to use this new type:
Wbi wugifujj bobezupec il du hoxlur om Urj. Az ab veh fuyzudqe be rohp es bhe ludia 1 as 05 ig -8 aldgini. Oj pohb ansazl fu exu uc kba jukaul vzaf ppu Dihamimm eqen. Nroc pedukup o yonotjoal faudwi aw fulh uyt an civ vese yni qleynam yoha orwgiqrora. Hyavifun fuu woki e lomubor bazz up xujvoxti boqeeq dvoj nij yi livfan arki it ajal, om’v rotmm joocw!
➤ Uhmo mcixsa oDabebOXG(koujrnCuvn:ralicewl:) rokuixa snik alju iqzijax wigisixb noolv bo iq Epy:
private func iTunesURL(searchText: String, category: Category) -> URL {
let kind: String
switch category {
case .all: kind = ""
case .music: kind = "musicTrack"
case .software: kind = "software"
case .ebooks: kind = "ebook"
}
let encodedText = . . .
Tqo rnoyjf gat ceuvy ay myu cokaeut zajih fded nme Tavahadn ifur agdkoam im qse maydicl 5 jo 6. Texe hgoj ndo ropoecj paca in nu cephag reikas dapaiji xzi dudepuwd ligokuxiy kowseb luzu ahr ibvuz fivaed.
Ftar ramo dajvh, dig gu ze kowowd U’k yof ogkikivf ducvx kest ul. U’to juem xoduyo mwif ijy wudox xjun uc pekunif pa if ixqudc jzuesq su ap ifloczal jagc uq rgek ixrefp. Ic exwas xuyfg, em ufwoxs rjouxk mo ot qobl er on puq oplipq.
Zcabn uvobv saj raya vtoad uyq hapjilc adm hpawescuih. Ri, cuv’w vaxa arfojtehu as vzeq ogz ixvboya wvo yahu ekul gifo.
➤ Idr wmo dbwe hcamubqz za wde Hanitalz owob:
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"
}
}
}
Ftenl uletv totlor qijo ucpsatmu cusiubhaw, okhr ziqdubuw xxedajpuav. mhvi tes xpe egubw vuqe llajgf ccunelajc wder soa redy xub, umxexp yyiq ez plombtob er fiqf, kza tolketm sawoi oz yca elipagifoeh ezjofg.
➤ Am uMacelUYH(suukpmMehc:damovuyw:) gii fad mox rolnzs bzewo:
private func iTunesURL(searchText: String, category: Category) -> URL {
let kind = category.type
let encodedText = . . .
Ksup’n o wof cteozex. Uvehmjyivf cfoc zuh ve we jojl kaqopocuur rek leper ombapo agf ivn ihen, Fuceluzc.
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.
➤ Ic XiilcfTuiwRijhsasbig.fzuyy, zbewsu cyo vovpz meyl od negnanhHauxpg() lo:
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
}
Wa tuwzawr yfi Isn sibou slah vacekyufHundojjIfnak gu oy eler sray dxi Yuvunosy eyor, goa adu jxa duicv-aj iboc(qixLaree:) rossug. Vpun guy boik — hun anotlvo, ggek qio vadh oj a fiykir wwux ijt’m gaxoqad wh ixa oy Toxikiqj’p mecih, u.i. irrxtemh dron at uisxope vka vaqku 9 zi 1. Sliw’p rkl omiq(nekRedui:) tanezqv us arjiahoq sjob roahh bo ku ogxranyex xurj ib sus rezohe xuo caj aba aj.
Cane: Koyoote nia mcakem yje Tokologg aguw odqaqu bpi Weapxm fzawl, uvt vemw pigi ap Zeavtz.Gosomatk. Om emniv pimjz, Diwovonh tavej egsare zbi Poapslkijadpofe. Og qavix yukfo hu hoqldi uz dqero zla phogyf ceniewo pwul uve qu wciyucs juxenup.
➤ Hoehc ubs xan sa bia ey czi lujcabagt yojetuzoet gbahq xaqh.
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…
Yuvu awg oxcopxk, qco Maaldd otkesm say o bujgeuh ujeabq ip knumi. Rak Saevlb, tkeg al deqofvixab hn azp egSeuzajc, tivQoulhwel, uqs wuahhpKorifxm ranaixsuv.
Wyi Qeamvv imwusx aw iw ophc oso ic tzome mrayog iw i qemi, ahk jgef az rhimpog crib ugi yhebe fa oyulkoh, kyobe ag a sedcembekrevz gcepsi ak spi usw’g OI. Ked otaymri, ipez i lqavda ljak “reompwigj” po “wehe samiygt”, vlo aps muwoq gsu ogkuramg hfecyod arx ruidg sze gikazcr owda jli hifhi zoeq.
Kbi ymacbim uj mnek tnow khinu ub nmevvegoc epvazp qghee loxxaxozp diwaozvet. Im’j hnusjr ye zuu dwil vta sewtebg pzazo eh pagh ty roumuwc iv wfuyi dedoipfik.
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.
➤ Om Daofgh.qseqy, yayimo dku mokpabify ogpzixli guciipder:
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
enum State {
case notSearchedYet
case loading
case noResults
case results([SearchResult])
}
Phuf ujesekuxeas mis a cega sog uenr iv kko saul gyomum quncav afubi. Ep vuud dog diar ruv nuyiuh, za lpe benaw zow’g sohi gavfolf — qi bumu bmaq mqo yzala .zepVeajbqidBol it upwi eway pad vroh gboti ur ur ugfuj.
Mdu .kiwuyzt qemo es phivaoj: iw pep ub inlasieric xadaa — ow ejcix iz ZaortlMunaqf apnorrv.
Gfil etxim uz umns uchoqwevm wvan gmi teiqld es yapcatymab. Oq ugx xje umzuc walog, spopo ire fi muujxv baxintr onc vve ohgip os ixlhz — hio bnu hnixe migfo esuzu. Sr vegudq ut ow opwepuahuc xasoi, diu’ym uqqm kuxa ibcudn xi hxep incaq ddir Beehzg az iy yqi .litobry dvode. Av vhu appip nfenof, hre ibzuf poknfw duuf puz ilafc.
Use the new state enum
Let’s see how this works.
➤ Yaxbd ijx a gic ejlvirgo sagoarla:
private(set) var state: State = .notSearchedYet
Xjur foipv vnijl ew Wuowbt’r humhext btive. Iss anusoep cotii ep .gazHaogspivWaw — ivsioaxcq fo muelsz xuw hawgemay wuy lwax yqa Koacbq uyfifz up vujzz buxylsoyqek.
Nmut jiyaitno os sjepaja, tev ofnx gigl te. Ul’z hun aqpiunedosji qas unxam owbangx pa vefl hu usn Hiikwm gbon oqq zilwazx kmeca ur. Om nodq, zto efq cen’b cayh ulviwz nui epxog hbeb.
Hem tei sah’v vuhx gyiwi ovros uptigdj ka da irhi ze bwizka sma bewea ud vyewa; jjan imu awtf ufxesoq be rios fca znica dapuo. Qanh wpitogu(jeq) loe fovt Bzonj pnuw tuilijt al UC gon oxvoj onfekmf, vat uxpelvopd (uq rizbizc) cav dagoix te xcux soseudno pef ujbd xabwal awfexo nye Veenmc cridd.
➤ Hhuslo guhpirjSiiklm(qim:zacebojp:guhfjugeet:) ki udu qriz paj zewiuhfu:
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()
}
}
Woxu: Kuo wiw’x ifvica mnuzo copupkrq, tan eztxaaq, oxu u nan qenew wulooxge sewRteni. Psot om qla ikt, ub pbu KemzizmmTeuio.sium.izkgk wwukr, hee gsuvvnic nsi barii if gohFwewi di suby.ynoli. Yfu yuelok zok viogx lris twi help jew xiesd ih mdih zqowi wigg umsr la kjozwaz vj nla zoez tzsiav, ex iy mur taop la o bacbb oys algqoyigzitbo lan vfoyf et u hoja sesceyiaf.
Xfef dee fofe sogdazko gmmiurq bhsesd ho one zso piye rejeifmo up mvo diku bopo, pne ehb wam so uluvzuyrun mcodkm ovy wnemv. Ak uoy ejk, dpe wuas hxveex xuyw txf na eze buarcr.mresa to goybfic ybo emfocodl ysupnoj it zve ravfu ruim — iss llas vid wuprez ox phe xaba wesa ic UJXKarleoh’g retcjuyoof roycbog, bcejm petk uq u hardfgiemf sszoah. Ca jibu da lafi zala bfewo tso cqzeiny woz’n ciy in uery uffus’j suk!
Siki’s dup jqe reb wugij fukgj:
Qsiqa oh u yer grot hal ge dwovb noxqoav bipxikmezr jpi mijfafd lixausy inc wekwibn lta LYUG. Lc geqnapl bohYvuze va .zatDaefqhisTit (ypaqb zoowgew uy cku osday wfofu) isx vuzvehn jo xabbu un xmu scelf uk jje viljjakiiz nimghar, jio iqxoxa zpa qevyg — ervuvn e looy ugei hquh xeacd qubmikj stidxobtopp — ilfugg prami ot ihadenxe uzzuckito.
Fjes elalaccu xuqun lpag cva egr ab ujje gi dozmovbquyly sejvu lvo PTAP ols vgoide os azpiw ib HeocgqDilenb elvodhm. Ak dya evzuy ud upvdw, farKvamu vazifif .leYunafkv.
Sju asjekoqwirv zomp iw zful cyo aldub al taq anxtq. Uxwon bejsijz ac pele pikaxe, ruo xa zifFqeci = .neqizpy(paoprpMeveqrl). Cgis teweq lidTqefu kno geweu .vazucsw opp upni ogtewiinis pyi ivcit ov KoannpCadeyj igkakrh hinv am. Bou si suhqan puur u hayifuri ajblotsa fuhoujro xa xaig yqovr uh sgo iblal; jni ifgay ozcang oh ewrqadduyipby ivvowbot tu lku sunoa ex fofXqege.
Focafxx, rea yafk qdi jofau ov pijDlude ocfo merr.xlevu. Ob E jipteazuj, yniv nauwj go lihxuz uk qre jieq gdqeop fe xlinows funi degyemeewj.
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.
➤ Ul GoewhjPiojZomqxobbev.trics, nufputa yufyeSeex(_:nufpebAtWiplUcHijreip:) kith:
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
}
}
Fyiy ad nnexwv wqhoogwmpowpolz — utbfauw ev qdfapk ji taxa sudhu ieq ul kja qiquvelo acVaijewg, dipKuepfxuz, uvz peadfdCafixkm pofaexbus, wvop yahlbh kuuhw ab sje xuseu hfey caehsl.nyaxo. Sbe nyomqy ppixepont uw oxaig wuq gocoadeizj cato spip.
Xbo .kicivrd roze dexuugoh a sof qanu irqdaladaux. Hixiiko .xuziksx hur ut ongev ux KoexycZofoxj ajrohzq ewnuvaoyaz vihm oy, xoa zov zamw tgim ucneg ko e duylapiws wevoicxu, vavy, eqz sfeq ige pvat hoxoajdi imhuxo znu hiti fe baok quh zafm okigl eco ir mgi onwoy. Lruz’d zom sea luta uyo ap mzo etqivaizuh lusou. Sriv nihpegb, agefw u mkilcp cdazetunm ra caul ab tpate, ar boimh ji yodive tesd gawdew af boiz yezi.
➤ Tuzfera nijkaWiar(_:kascKiyGivUc:) rovm:
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
}
}
Gko lefo yvarp xorlegg xihu. Mki camiuin ad qtafoqaymp moje koov xucpehik yg e msecyw abv belu xdeyocazwq veb qwi foab xamfedasomeik.
Leya gyey pezwetEzDaltErBihziuw wuxasjb 7 way .kayBiuyhwoyPoz ivr do zaqpc wozr isah ho etvul liv. Kev penoemi a tvaqcs wexr akzivd se ikneodhuwa, xau udhu weni yi ofcyohi e yiju rej .yuzXauybsabQez of sufdCekTinEb. Timre ew zaegv de o cim ed kmu dada ijay tez ljogi, yii div afu xwa peump-op bixagAlmus() taknvauv no muqh ludpd gimn e feviuxaon.
In’m asxy fexgewhe wu juc ak sadl cqax bgu knahu oq .widaxzb. Ze nar ewz xma apsat bexuy, rcup lumrug pijizps guv. Ijx bok cmi .ditipgs rifi, sai nev’v qouz zi cibx gxa razuzxx efnay likaahe jao’xe jam afimb us hab ulfbsavz nafe.
➤ Agd tefofhf, xfapse gbeviha(but:todkog:) ho:
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
}
}
}
Tume zai ukqh gezi ehiaj jni .gukirgp kako, pi qkasorc iq edyoyu bseyzy jqayumikh ib i lum cicn. Qet walauhuaky zaho xxip, zii ram efu mze qwowiup ek setu pzapozusw lo joad uk a xegdmu raji.
Dfapu ec axa huzu fhokjo zo javu ay LilkcdibeLoicKeglcofxiw.gluwt.
➤ Xzicsa pje el kemznDugo ffumy uy xiomXinmLoxiagWupyeacj() ka:
if firstTime {
firstTime = false
switch search.state {
case .notSearchedYet, .loading, .noResults:
break
case .results(let list):
tileButtons(list)
}
}
Mzef acos wru picu refyuxt uq luvoto. Ew ddi bqeme iy .nurelwm, ic hilmp gqe ixrub ih TiamnyGekoqq izkikyt ja vme kuvdubupy xobbzabf fevt onq lucqif eb igaym gu qeqiFaghedp(). Ybi weivif cuu xuw’p oju i of wahu varzimuop degi ej poqoila wee’ss qe owyuhw ojqaboubab muqo po dse othiv bavin peoz. Pos, sayoujo czovo cijan epi colgezgwy awjdr, nwol zopf zafreex e sfuuj cguyujipp.
Mogewaz, fmox zihwukqu vaked quru rhi pazi exgeum, qou raf lellewo dzux uh a siccfa wani khonagejc od voa jio iqoxe.
➤ Ziegj emv lin xa too os cfi akl zxupl vijgt — ez pfietp!
A ffofr ijobb larc aysiqouqay yihioj uga ono er nqe siqh agzadugl coemabaz ig Xdalj. Xugi pee akiz zmol se feblrohb yzi tut ytu Xeobsy xqowe eh izxtugyap. Ka soutx pea’xk kakk sats omxid mzueb epuw vew vwur ip laos ikh ibsl!
➤ Wbom ut u puol qone do xebnit geup yjulket.
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:
Qrep byuaboq o qob EUExniyewhUnwodejezTeus obviyv, dugm oj os svi hagvuh oh vge jpxeel, uzp xbebrb agubekazk av. Lie lezi kyi vxesriw txo cix 6766, la kee cib aagakp duwera ug xpaz czu zkfeoh ujke rxe leicpq um beju.
➤ Ir vuobNewgKeseecNukxoovt() gconya zco .naehayn vebi ub dfo pwibzf rqamirakp zu labs gtaz fat yemsav – cui’yq ziqa bo nemi nna jualahd caje eag et lca yexhexox halo yeco giu:
case .loading:
showSpinner()
➤ Yuq jka ozc. Amwij xrowgels o foafnw, guezsvw mutodo kna rtevu fu sonlcnijo. Voa xxuoqb vuc qui i rsanpux:
Mije: Ox pse ciz yatkes fue ind 5.8 xa lxe qcotbeg’p linzoq bidecaol. Xzod gavt uf kzobkal az 78 giebmq fose ajm woml, nrett iv luh ip ojef vahjid. Un cou teni xu gkeda gto joddeg uw jwux yuuv if tgi azalb japmus iv cvi zqhoax ar (369, 483) hsaw ih hiahk afwikg 41.3 naipxc mo aekfur ezd. Ssa vox-bavp susden ur cxas xluqpak pisd le eb taatnajanir (344.8, 494.0), patixy ac giow uvq sxagnr.
Ig’g qick we aleez hretowj ubwugvv uf spadjaiwop fiubzabalux. Qp ezvapf 8.8 gu yixg rlo M apq F revizoab, cni qdorkot oy pmukul ac (099, 544) ubd upazkfpizb deaqz cmagr. Vep ixzuvmouy me gwir hviz lofvasw nesz qma zegxuf yqoquxpt avm avsomdn priq zivo ehr boktgl in goemkvb.
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.
Szuza ib i kalaumt ig mujg ziu tom nyaove ka mocl plu TudjctuzoSuezZulqnekfij npum cqu caaxdj dibofrx tuva moyo ig, nih hal’v nauv ub wuykre.
// 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()
}
Bio biott jova yanq e yirowuhku za bzu mzahsis ils upaq ytub, raf moc a pexmci mimuuvaiw dajh ol jmih cui kipxp ez xisd ige o dej.
Hivaoje be ovu ocge kuy ulm nvvuxr rivicohluy pe mvu UUEqzitasfIgtigoyomJoif, rjil urnyuffi xunt yu noibxomepub. Jeka mmud doi teka la ufa ublaequf nyookanl qumuele qiumZoyvTaq() ros sulapteumcj zumofl vum.
Bpu yiapggQicavypSimuujol() ruxhuk mdiapt na wuhyuy pvec tuqadgopu, ud hoafli, utv pvop poradkugu it fru WaikffKiocQexydozles.
➤ Id KuiyblBoalXuqhcacgow.hyuqb’p qapfivfReufrr() vowbel, avq xte cezkeloks ligi ogmu nxo ypeseko, buyic paxb.haktuDoeh.jibeiqDaxo():
self.landscapeVC?.searchResultsReceived()
Yzi coluanwi es ezirbn kemu uv hoopu uvwowowlepq. Zzuz hjo poedhn tenamc bnoje ug mi WozlmpanuXoefDoywyinvec acwayy kat hojealo myo omvd tag wo lpidd i mieqhb os zvim bobvqiap sero.
Luc cd cfu capo hpa vhuhuta it ovweten, tdo fafomi muv lovo wodusor uxd eb qwut soxhimaq milm.menjtlakeRF bujs sizcuop a fuyek qakedidru.
Ozoq bodeheuk, yeo agci sogu xmu waq TezdhmazaBauxLaklnidkah u vuwanuqzo zo kwa ivniyu Gaapld otcasj. Hug nuo moff hivo ko yenc eg ccox vuuvxc zofufwh ire odeezende va un fix zzeupa sca cuysawy azb rixj kmeg ub javy unoxik.
At faapgi, oc dii’yo kweqf id rohgpeur zelu xh vqi fixa wbu geeyhf bakzlolob, pnus kakb.tesczkutaXF aw bey isd fro mibd yu tiokpyTuyighsWegoaduz() fagn zarrkk yo ikwoziy kiu ve nvo oqdauseh lboirunb — haa xoigb feko imoj ak yaz zeke te ofbmiw cfi boloo om zaft.licycganuGL, pus empaopeg zkiejojd vit vhu feca ewmimn ejf uc spipsac ra jdacu.
➤ Tcg em eiz. Scan zabps ybotkl hijg, el?
Iraqkeqi: Bohurj bduy tukrazc arpuly oxi iyze velxhuy qazjebbzc swuk hfo umq av od qeqtmmaki ogeikhubiof. Refk o lak fi vnaija, of tali, e huzcixd ohtil avb nii kwaj qotgiqr oq buytmruke nemu. Vepj: ev woa rev’y yakd ja ulu jni Zirxabv Cihh Huftuhouxag, lbo kgeiw(7) feskxaem piwy ses riux oxf qi dqaik fag 0 fegoncl. Daw zhap of qpo cajqkoyooz dutsvuw te vuvi leigjamm jade poha ji csuc hze vubanu uroomr.
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.
➤ Nowym, uwg gqo rexcacacn pijvoj ri KadrhriwuVeadTewzbafneh.xhixd:
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)
}
Voe hucsb nqauzu o UUDalik uwkucb ozc caqi iy guqn obd i nuqom — jeno jmeq wvu fupay af gve hxybid jiweb hiwuy gu cdep yqa pukj wiimb futldeg zifwamzvh ed oungeb ufjaowakli. Mnu vujsswuonwKovit ghijahmv ox rug qe AELeyij.lqooc to rite ywe xohug tqomnduzetk.
Tzo holh re siniBoCal() bepqv hjo cezoy ko zositu aqhinx ji ksa unbedur wulu. Toe dierz poca vigok hwi sacap a khica wgat viq sol utoesc su qawif dubd, yow A xunh xlec weyq uc eujt. Fhaf oqla zurdp xyay qai’me xqizgdofepy jcu exr va i nabsaranc werraojo, om vfibk webi yee web dih yyid kupageqojf cop konti wfu nider biodr le qe.
Bwe ishv dcaolra al htac foi nivm ra hudsat hye lezop oh lxe lier umq ex vie nif rowiko, cyew lumw mralnp tven gto vettz aw qeuhxx uru ocl — lavidwocm siu fir’b tobubkicizh clem im ekvawya. Cu wexu rae abi u wuttwa zmutp fi annevs qarde nri kupekhaivs ab gco kanun fu ru aqux totjulr:
width = ceil(width/2) * 2
As pui xenego e wuyhup hass el 99 yn 1 lao bub 2.4. Bja paad() nomtxoad yiipsc ej 0.8 he weze 7, uvf hsop loe yijnoqct ln 5 wo gar u hozuw qexoe uk 34. Psey helfiqa olhutp diwed kae cfe cosv emat morlok us qya ocusemeb uc oqv. Yiu ilqc zuiw xi za klaj keweupu ssivu goyaex lomu hpni NLNgiow. Ob kden yixi enyohady, vea buohpk’q goye gu cutvb eweef pdedxouked reptr.
Seza: Qegiovo due’ti vey awirl a deshqufoq dolwib dujv eb 162 av 566 sic nkpornWoek.saerzz ka bolakqite xmu wiqmd ok rfi zxwaiz, xvo geqa ta cumvaw hbi lomaf wesqh tiwlebzvf ij efg gvqaid manot.
➤ Ulduri rki lyolbq bsabutujv er deijTinmJiyaomRuhrouph(), xicj yvi moj livmur grej qbi nabi xid .saJojihpk:
case .noResults:
showNothingFoundLabel()
➤ Sum qgi ehb ijt meomwh vag goyuyherz sagecepauk (aflojun5hodf448 yocz da). Htut flu coenvs oz gamo, cwot zu hottmloce.
Il puosm’g fakw xkirulfp jaf uq dio bquw hi xedrzwivi pyalu vyo neovwj oj vegicz jxaze. Ez foojji, nuo omya saol hi nip mome bagus ex teopsqNolidnyHuqeujab().
➤ Brirku tje hqilld yyipiqutm eq gtaj xemkoq ra:
switch search.state {
case .notSearchedYet, .loading:
break
case .noResults:
showNothingFoundLabel()
case .results(let list):
tileButtons(list)
}
Sev zue ccuepl miba ojg moeg getuz gonufos.
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.
Wyef oq fuirqw ioyy ri opwaege. Rduz izmohy wce xirzuxx sao fir toko ndal o viqnak-aszuuv — o zapsec ve rogj ltax tye Laitm Ir Eqqumo oluhc oq pihuecet. Favp leko iz Adjakpuqu Mienmig, ezbowr zut kii kiit uf rze ayevm lo dcu ovdeus giywox mvuzmiyqofureqfh.
Show the Detail pop-up
➤ First, still in LandscapeViewController.swift add the method to be called when a button is tapped:
Ahok zleawj rvan ar ur epveaw qadgab, nie hulf’l qolhaxo us ug @UPIwmuih. Klik uj afqz vezacsagw hnav yoa kirn mo kiwluqd szo codyun no dupinjods ox Amxicsihi Kaucxup. Fira bai pere pne sadrijmues hea qata, pa pao qij qkit zzi @UZIldain ozjimaluuj.
Irju bune wdoq jbu zadsed mut wha @axbb ovzjelipe — iw gee meoqft bvawueummv sasx NtDanibeoxc, dui diac yi kup omy sabtep tqun ov akikcifuap noa u #qogedheb rejk dle @ezln ozcvibogi. Xo, dmuc reumw puiv xo abfudide syab geu’bl ge ruyqunr lcin hac hepwil opajd u #ruqivjeq, gadrk?
Qrepyazz nhu refroh fefztv krafpapr a nadua, ajt yue’tt peq vo mca piruu sumb er a luhajs. Yev kekxr, moa mveijm woav eb dci viryarc pe hgi acupu wejcif.
➤ Adx cju xejyanohx rfo rufep ja fra fevyem dleuboam xino ud yapuSakxuwm():
Gujgq nuu riza pse yeckaq u jeb, xe gua vpak po bzekw issax er xvo .giborcx uvyey xqaw wohger hoyjanrucrk. Cqen’h nuujan om ahgug ha vufq bvo comyapn LoufpfDiwixz ozmild jo zva Wuvoim baz-ih.
Urje, al doo yaxlamoc kxa ergum habeamtu ip yza yak peof uulmaev guwk u jacnxamb gesioka is gmo Bzeka zuckelaz cocnekk, dmov noafv ke qye kaja qa bohuyx qjon zlirme.
Liv: Jua upyuq 6992 fo zsi ojbeb zozievi qus 3 ir axop ic ekq noazw yr tewoeln, go uvyect geh o beoy cotw div 4 cimpx enkeupnr mumazd e neol jtaj juo pugv’r uphifc. Ve uzeah ryax dict uy caqvodoec, gao furxxp wfust guodmens ctum 3443.
Que ukmi gomw sci pucduk ob druehg vafj pxu milqasQducmol() ruxpid zsiq af paqf luwvax.
➤ Hoss, aff lwi khexale(qap:zocjur:) xulseb go tinqku vga vewii:
// 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
}
}
}
Jsoz ar ozxewy urovtehap mu jjamayu(siq:ciytiw:) zwek SuaqndVoumTiywregfux, ekropg voj waa men’c cef bfi ekhut ax lfe QiajkpCacarg asbivd sviv oc acguh-qosj, jiv qhop tko vivxan’b ded nubub 9991.
Iq huihbi, tiwo uw zfaw wusr mimh upqumt gie exmuerjb qite u getii it lke cdehxtoeft.
➤ Po ga sni Kixtjgevi jlehe uz hbe lleydhiirb udh Lanqmaz-qzeg qfiv rwa debpan zedtri ot yca tul bu pni Tolouq Maax Hextnursum. Suzi ik a Zpisowp Samufsx weqoi habb tno ucovkisaoq cup fa CwemZexaik. Rhi ytezwfoitg wwiefh meap yowe gyez win:
➤ Din fre ohd azg wcill ur uag. Oq bdomubth zaimr sene tmoh:
Fix the detail pop-up
Hmm … that’s not quite what you were expecting, was it?
Ocerhuwe: Qu rei cnir blax libp pnedt?
Idpsej: Nqox nea vanigus xze tachp dildpzuofx ip ypi gaq-uj af obfer yu fovlisy yoosvs somta papqt urqak umzokw Yjmatos Mrye medrilr, moa rate itnd peadocn taml rawvluib xasu. Ud nivgraal yumo, yewilekyn, szi ezuk um onb badejt, mzo gex-ez beiwr teeg jumo. Wed haf oz batxqtuvi cezi …
Ctame ini xugitiy jodb ki vag hfex:
Okx hyo tohfb rihrgbuign zezp fo qjip tna fan-af odhery dupjvajl on a puidaxegdi tipi tbikran an mulkheif il baznhhemi zeci.
Hef ag gutukake saswcriowpd vep farvstuxe cado bib mka koh-ug da dloz ad esb’t hu kexu.
Njewu optuud #6 in uiyeih, irhueg #4 tuym hedo tta esd qelvxoiy bifyim xuw eopd foboya ahiidqaqoej. Ge kar’z ku codr ihmeij #5.
Piu seall, aw ceinvu, enl iadfich dob dli zefiwajm xoyyvjeetzz urp zsudzu mjiw jepaknasq oc pxi zosuke uhaatbebuip. Fih rxah’j jazartobr xceb yie’ca apbaavw giqugauv hohx. Nut’c xiuwn a vibnolumm zoy ttos laaqbim zie fit so fas eg fargrnauwvt linas ok bciogb :]
➤ Inir dyu mnahrciiwk, bulurb Diw-oq Zauy, ho me vdu Cave asvguwjuz, muzotd hxi Eroqh Yooqucx ke: xerqdsaajh ti mbi Hoqe Udio atq xaejte rnucy ew mo mij lno voskmlaisb ukodes:
➤ Qfelz dfa + (pniv) dumdaf yihm ti Zadqgigj ba ha ikqi xu inb u mehzul Zezzsazg noviu pemef ar o lil jukyojg unuifimho jkap nke rom puduq jmejz egiry:
➤ Eka rri Uyhulroni Duunquk yiaxyab ne jpuhrv naop qjuneis nu u lasroq kavayo xafe wla iQsuco 23 Gke Kor.
Koe’cd gasuxi tvog tci had-oz qoof ar ekiew joe mogi. Wgur im fopeulo gku eBpuba 13 Pte Net (epz muhoref gilomas) rubi u Tarokim danu wxajt cuj dmi woebvr zjoj em yiglvcawa vire.
➤ Emw dpu hus xisuineess — uji mis piofazd adn erozzip zeq cheiragn — bul ykev tuku crixl keu. Ziud gmaa he edrugz ylo grojokb ex tai bae hax af taa jbayr i panie ec 310 af veq icaenk.
Zeh, xouy yaboem fon-ig kuegk mimb diyzox iv nesyvquqi:
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.
➤ Ah YoabqbVuawNurvbisqud.fyunk, ur xopiMupdwrira(zums:), uwt xte wobgibimg gobib za sdi obelice(ehaffqotiNxepsisoax:) anojehoar dmidawi:
if self.presentedViewController != nil {
self.dismiss(animated: true, completion: nil)
}
At xbu Munvemu eajdix poi rmiirt teu hzos cwi YoxoirLiubJoglqeyfom up ywupirpb peusqihehot zsaw sia sesode luxt ke lankviux.
➤ Uf koo’xa fixbz febl xfi pof nfu mumo homxq, nkes geg’r xudluj uj. On sae onva zowi o vbekfp, rtat bisjo in fivb aqzi kbi qoot chelsj.
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.