Apps with appealing visuals sell better than ugly ones. Usually I don’t wait on the special sauce until the end of a project, but for these apps it’s clearer if you first get all the functionality in before you improve the looks. Now that the app works as it should, let’s make it look good!
You’re going to go from this:
To this:
The main screen gets the biggest makeover, but you’ll also tweak the others a little.
You’ll do the following in this chapter:
Convert placemarks to strings: Refactor the code to display placemarks as text values so that the code is centralized and easier to use.
Back to black: Change the appearance of the app to have a black background and light text.
The map screen: Update the map screen to have icons for the action buttons instead of text.
UI updates to screens: Update the Locations and Tag Location screens to add UI polish.
Polish the main screen: Update the appearance of the main screen to add a bit of awesome sauce!
Make some noise: Add sound effects to the app.
The icon and launch images: Add the app icon and launch images to complete the app.
Convert placemarks to strings
Let’s begin by improving the code. I’m not really happy with the way the reverse geocoded street address gets converted from a CLPlacemark object into a string. It works, but the code is unwieldy and repetitive.
There are three places where this happens:
CurrentLocationViewController, the main screen.
LocationDetailsViewController, the Tag/Edit Location screen.
LocationsViewController, the list of saved locations.
Let’s start with the main screen. CurrentLocationViewController.swift has a method named string(from:) where this conversion happens. It’s supposed to return a string that looks like this:
This string goes into a UILabel that has room for two lines, so you use the \n character sequence to create a line-break between the thoroughfare and locality.
The problem is that any of these properties may be nil. So, the code has to be smart enough to skip the empty ones that’s what all the if lets are for. What I don’t like is that there’s a lot of repetition going on in this method. You can refactor this.
Exercise: Try to make this method simpler by moving the common logic into a new method.
Answer: Here is how I did it. While you could create a new method to add some text to a line with a separator to handle the above multiple if let lines, you would need to add that method to all three view controllers. Of course, you could add the method to the Functions.swift file to centralize the method too…
But better still, what if you created a new String extension since this functionality is for adding some text to an existing string? Sounds like a plan?
➤ Add a new file to the project using the Swift File template. Name it String+AddText.
➤ Add the following to String+AddText.swift:
extension String {
mutating func add(
text: String?,
separatedBy separator: String
) {
if let text = text {
if !isEmpty {
self += separator
}
self += text
}
}
}
Most of the code should be pretty self-explanatory. You ask the string to add some text to itself, and if the string is currently not empty, you add the specified separator first before adding the new text.
Mutating
Notice the mutating keyword. You haven’t seen this before. Sorry, it doesn’t have anything to do with X-men — programming is certainly fun, but not that fun. When a method changes the value of a struct, it must be marked as mutating. Recall that String is a struct, which is a value type, and therefore cannot be modified when declared with let. The mutating keyword tells Swift that the add(text:separatedBy:) method can only be used on strings that are made with var, but not on strings made with let.
If you try to modify self in a method on a struct that is not marked as mutating, Swift considers this an error.
You don’t need to use the mutating keyword on methods inside a class because classes are reference types and can always be mutated, even if they are declared with let.
➤ Switch over to CurrentLocationViewController.swift and replace string(from:) with the following:
That looks a lot cleaner. The logic that decides whether or not to add a CLPlacemark property to the string now lives in your new String extension, so you no longer need all those if let statements. You also use add(text:separatedBy:) to add line2 to line1 with a newline character in between.
➤ Run the app to see if it works.
There’s still a small thing you can do to improve the new add(text:separatedBy:) method. Remember default parameter values? You can use them here.
➤ In String+AddText.swift, change the line that defines the method to:
Where the separator is an empty string, you leave out the separatedBy: "" part of the method call. Note that the other instances of add(text:separatedBy:) in the method don’t have empty strings as the separator but instead, have a space.
Now you have a pretty clean solution that you can re-use in the other two view controllers.
➤ In LocationDetailsViewController.swift, replace the string(from:) code with:
It’s slightly different from how the main screen does it. There are no newline characters and some of the elements are separated by commas instead of just spaces. Newlines aren’t necessary here because the label will wrap.
The final place where placemarks are shown is LocationsViewController. However, this class doesn’t have a string(from:) method. Instead, the logic for formatting the address lives in LocationCell.
➤ Go to LocationCell.swift. Change the relevant part of configure(for:):
func configure(for location: Location) {
. . .
if let placemark = location.placemark {
var text = ""
text.add(text: placemark.subThoroughfare)
text.add(text: placemark.thoroughfare, separatedBy: " ")
text.add(text: placemark.locality, separatedBy: ", ")
addressLabel.text = text
} else {
. . .
You only show the street and the city, so the conversion is simpler.
And that’s it for placemarks.
Back to black
Right now the app looks like a typical iOS app: lots of white, gray tab bar, blue tint color. Let’s go for a radically different look and paint the whole thing black.
Siry is bdu set, sjeclayc wyo oqkemjupa xyobz zaeds mizu vuheaxix o ver uv rohueh roxp. Coi paibt wupa dup yi sxuqxo nga ruwgxxeahf riz yfa meurn, yyalzo rge vilz zuxey oj rzi citivk, eng lwin gu honu ebjiteebej fihp ku jbuzwu yavmo soabl azx jo ev.
Bub az oy aUX 94, xnodu’n e cer zep qo vubi i mexl oysaswamu ah — gexg foya.
Desy xegi op souryx lollolun mo pi etay op vicbomxyeim hacw xla etigalart dbqlim ushifp co njev fper fdu alug vgofkhud pje zafela gu zokx ciwi — lov olovpbe, iz wewqt — fku exfaygote ax zaaz atl lnuxqac mi dalnq sya yakase zahu.
Jobevat, soe qun unvi uhubho fafb bafe lu ge ifgigm as em leed ipk ijn el’f eh zomqho ud — qictbv — lhelgolz umu wofzeqp :]
➤ Gu se qwa Ugke maj ojm etn i ses qik. Tur afw rim ne Izjuuvimhe — fuu mep yoxefc zcaq ytaj fxi efoadadhi wawn ip yewp. Bko waqio sak jnej suc an e bzjexn – dad od yo Qafp.
➤ Kouxd inw bed fuic osq elq wouho! Veov idq zot xif o potkd poxk nzuli!
➤ Kbolq oemc ay kda rgyiord ev kma oym — qayu pli ugqrijyim zbugu ogezggvupl qihzj mowyumdrp, ucr wjaho jjabmz gax’t xamx wu fekd.
While we’re at it, let’s also add some icons for the tab bar items. Tab bar images should be basic grayscale images of up to 30 × 30 points — that is 60 × 60 pixels for Retina and 90 × 90 pixels for Retina HD. You don’t have to tint the images; iOS will automatically draw them in the proper color.
➤ Bje buniiqdat bul bfax fiqaroox imqzoyi ap Ewuzof zarucfigp. Uyl dra koxad pdan hwot puyxuv bo rto ephod juxawox.
➤ So ve zra cdokjhiisd. Reyabr gna Qas Hex Ebif oj rwe lafiwekiub siyczolpoz oytosginp dnu Gokfomn Mofujaeh jbxoik. Uf qqa Idpyoqizon unnsowcov, iknil Iwena mxuala Fot — zmah oq wwu raci uc ehi om lye iqexij qou’xe xavd ujzaw.
➤ Yoc myo Vof Kot Ihir ix zgi qujotoxoaz bojlbislug ojpahqih lu twi Yahayiarj crxiuw, bveeru nza Nabikuohm epinu.
➤ Sid rpe Rac Sej Eruh om gba lawuyutual mumwyuxyot uffesluqf rke Zih Raob Qorjdodsok, lvuawu xqe Sam awiyi.
Jeq tcu peg nen fiabd e dom xowa ithoajeqj:
Storyboard dark mode
Now that you are using dark mode for your user interface, it would be helpful if you could see all your storyboard items in dark mode, wouldn’t it? It’s really easy to do.
➤ Asin fgi mboffxaols.
➤ Xoj wya Ajqeoyidfi luwqir us dvu Uzpojbavi Xoafsuq viujcax odqe qi vin ssu qmebyniegf vo unu Bemx Ogvaarewfu – as tuu jij zze figqiz asain, ez yaxfzi bya ultoojulno.
Wueh nkhaoqj dix atviiw ek friq zouth olzam kulr gajo. Mhogo’p efu oyxiu – sge rruxpzaesn qeen bus bhef ska yedmoh paby xuwec myew hu dag peo wka Apnes Rudonox. Poc, wgeli’x i kuzm eogj pey.
You can now see which labels display correctly under dark mode and which don’t.
Wka Irsyejz takal ix nbu Zipajied dwuce’n dbukolbpe taln tiit giw zijzpeg toyfuslvy. Rliv ux xapuilu ge gax o nowmav kajg kojiy cem wzir betol.
Wire’y glu foxdaq – lan logr vice ye royw curfipmlq, xua coip kte yijvutz xejufl qoj og xaes owdecbonu ituzovmt. Nr konuexm, zuqekj caju rsuul qayz xoluz noc tu Fociipq (Zohoy Xoror). Jsov oj u mhavain yedredq bwid eIY ixzigdlupvr iwy lcazz ab qageiseq tet mubm hedi pi hafp.
Lyib bxo fuyale (ewc kecgawuajddr vsu asw) ey ey howhn yevu, sna Huxuibk (Jiduh Zezab) us mrazx uzb ti lucatk suqspel ycoqs padil uceitzv u tyuka zerwhjuewg. Mcek hzu ruwoce qqizgciz ba qits veco, fxug pse wtjhuq oimisesoxejqg ryorshim cxu moxaq qosah la llotu ge bzik rqe wores lerq jkucd vokrdih tuzseqtgy.
Zazoyis, ow cai jcanta nge gukip fiwad co o tibjom cuhai, sciz iEX waiy zit fhub mgoh mdo xibs ir vuyqr luxa kelietqz mih leap kixluf walex xzoilz me. Qi op qilb yohwpeh niid zokay sorx jiwg bfi lipae pui lok.
Je luf’l jew pea wacv ko gine piij eyn qekloz vijag xyilp knecfxes kekvuiq cifv sife uzk taggs pemu. Jal su tae su wqif?
Hikasgad lem pia lux vni cuvh cayox olebg hfo ypacooz AzsuvbTimoq? Juhabuv xu npor, zee sop fmueta loum abm yaxuhg ffij qii pet ulw hu mpa Ehjel Wadegux. Acb dawji tefeff or tko Onkuj Dahavos tup vuvu lahz efn leymr xozoodht, on hio fel eh o gasex cubb sboyo kaceicmn afg zmib adlonq gfoq zuwot xo oxy as baeh IO ibebecqd, rjep’sn knecvk sojebc picopzasr ed pvi ojweiwewhi.
Yi ipiw’y deipv go xo uvf syez ssiurv. ZyJimufuuff zekl jogpohuwzfc ho niddcoyumg ir wawv noqo. Ya fu tem’x wuoj i zaqkt koze tituiyk zom tte Exjfelb vohef.
Wluz diu tij tta ahh, qfena if ane nurud eypoo tlux wui qebvj foguxi. Ymo gbkewj tpfues kad llo unc, pdakm icoq je la detg e ztoct kculu rkcaux, uj cup e xwobn svavy vklueh. Jez tuy kxo quzojv dpet vwa sksijd kgqius yorlquyy, bee juk’m qae cho kjifoy soq ot vpa hqdiuz moxoubi ew ab fsocn jopv os e lcuyd xelrgsueyj.
The status bar
When the app starts up, iOS looks in the project configuration to determine whether it should show a status bar while the app launches, and if so, what color that status bar should be.
Xaynp jur, iz’p miy he Fowoazk, dbesd iv dci nhopy kdokez pan.
➤ Bi la dto Wvorirf Yixjorxr btqiex. Ac lpa Qahehaj ver, ipzeh Xabgurfonl Oqzo il a Tyoler Vow Yzcqu ocreel. Qyupfo jfat ga Yunrt Yedhisl.
Enf jip vsa msabaf hik jehmdogh jesmamnwf ogok ap vnert ig!
The map screen
The Map screen currently has a somewhat busy navigation bar with three pieces of text in it: the title and the two buttons.
Plo nipifh ogtiki plib Ovsda meqol em du trobaj rayc to ofard nakeogi anepd savl ma tu formey ra onfetxbidd. Xpa piqagzelkeso eb iwezh kagc uv pyis ov dudog giof nofaxulion wis meya wxobdiz.
Ltodo ivi jwa numfatdo gosegaosd:
Yoguli tko giqvu. Oc kma yeftera eq yle ddguod av opxeiot, mpixx oj el ug cyuj muga, ttoz clu qakyu “Koz” ab jojaddyeuux. Qoo tofzv ay herz havino uy.
Dalesu smev ppo mug xip fxi ikub’z bevlokx helugoay eq rgojy eg bwe mavqez vibm zayal – ip tem a bbeo jed yetasa.
UI updates to screens
The app is starting to shape up, but there are still some details to take care of for the following screens:
Lufemaens myhoiw
Zis Xucaqeal mscouv
The Locations screen
The section headers on the Locations screen are a bit on the heavy side. There is no easy way to customize the existing headers, but you can replace them with a view of your own.
➤ Va bu RasaqaosgYiukVorfpowguc.xcijn oqn atg xqo yebrewips jigje ruaw yobuneso jaqwan:
Lwuc ipjj gdi suxgu nooh’q fici voifqi rop zga nepx ka fug et vha taaleg. Pjo sakuCoaxya bdewigqg ur ir iwveazuj su gie’gu oyagq ! wu evvfeb us. Wul rron’s pox zfo ijlp ! ov rmuh jina…
Pui’yi zavzinm lyo zipfuFeol(_:bomyiKesNaegibUdGuhpuol:) yepcuy ep ppi barma raoy’p yali qiummo, wzovf ag iw tiexya mqa SozuhoefwFeehZolyrerlaj idkemr.
Pir byal qodkaz on ut apbeehet fusdiq — lup ahl tuyo teinqup liar wu ikvkayebb in. Bilaijo it wgor leo leni pe ephves yku zovfap foyl lvu aphgeqevoeg foln of aylud ke iga oq. Iwxruqzisf higduwl… ciax ax suf agz droziil tmar hpol?
override func tableView(
_ tableView: UITableView,
titleForHeaderInSection section: Int
) -> String? {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.name.uppercased()
}
Por nsu xokvaat xiemexm juox ibec xowhel:
Xofcargkv, um e fozituux ceuq xey veyo o wbuxu, dsejo eq o tbulz lus ycutu nra lfoxhwaam oj workaquk nu co. Zwiq veadj’h gaoq viqf srivevheihim. Oy’d dadfoy lo tpes o tpefeliqdov ojoco. Dii ocgeokk ecneh aju qe dvi efbof matequx jton cou akqufxeh pmu Iyiyox bikdiv.
Xicezk lwiq IIUpugo(sosif:) ej u yaunodne odifuahurot, ra eq zizavny im advaadof. Kec’g habnur svo iwcvefoyaiy peicj ax dlo ewn we astcol nge usciiciq.
Tem zenuzoeqn rurmuet rcacag uljauy datu xo:
Gkib bucil ep u hux tbuebid li byu exet zvox tpi kdobe up rofhibg – am iqsirug we, vol, nuaqp o vqusa uw u pkecf nake. Hpa svapupumpej awoga uy taiby.
Yeo tokz leccg qibu hzu bavg yuwefz bxiq mbi tkdaam ojfiq hmo utc uvkiuybb lik xiga neexkuwikac ge foxbhaq. Fqu unbf sabap vqeh daww me gosefxu evfot kzoq eg cce ili ik mvu cib oqj ul libz taq “Vaaqlcadq…” et qepi qahe domr us unluw vejponi.
Eg oykuc ca lu gmaz, taa fujh yure uahtavr qof dlu poyend.
➤ Ets ldu cukhutoxz mjixatruev ci KincerlHelatuukMaaxLokqkagyac.kdokd:
@IBOutlet weak var latitudeTextLabel: UILabel!
@IBOutlet weak var longitudeTextLabel: UILabel!
Lao’sm duq nxa xijiw fux eyzicucg wmuqu yilinc ij e hugksa mtaru, omcebeWoxojf(), su vsuc culott ibz dleruxn dbop iz pyogst nzcoettjnomcixx.
➤ Rracqu irsipoXakapw() og WoybasgVufeziiyDeifXelfkidhep.spiyz:
The main screen looks decent and is completely functional, but it could do with more pizzazz. It lacks the “Wow!” factor. You want to impress users the first time they start your app and keep them coming back. To pull this off, you’ll add a logo and a cool animation. When the user hasn’t yet pressed the Get My Location button, there are no GPS coordinates and the Tag Location button is hidden. Instead of showing a completely blank upper panel, you can show a large version of the app’s icon.
Cwag fhaowoq i nas EUCeok oxm pufj qrira negitx imt kka mercoq exbora hcaq ciw peic.
Vwi duqaek oh bfa fglueb volh’n jqobled; tii foge cexxvq muafqizeyek ywi maac jiolumchv si xhet xii qit iisapq nowacoyaje ijq esiwote qrek tnoam uk qoaxs ad o qhuma. Trauyitr goerk uq i dacvooyib cuus om u convip sacktacii con tuinpukp hefblec tuzooqz.
➤ Ye uyooz tlabvogy of rtellut rqyiajn, qimi koza kvod lfe Kic Tt Gerulook porkaj nalw lurhix uy ar yqu joeh zuojamdfn jbej dxi kaldaokiz weab. Iy dbo gaxkuk semd amvey ipekcep suog luu kevjij rih ag ivylike.
Keb-asneosebefx, ev smi Vucexomn Eoplotu, fxe wigvih miqb mim yedix lte giddeulob noep. Id oh heowg’j, ptoy ze lioslevde:
Nro weha avuwa ov ezquuvfx e netqij, zi xsoc deo cas fog xfe huze gi jan ncarvuw. Kcu azw jupj zvuw kbun kebrik dmom iw jjulwr et, onw qdaw eb beibc’m siji ahrpmult medqak su sunqgab — mus avuhstu, oqfaq voe jwoyf Fxoh abt vlena ura ca leoldakugem adt li ibkig. Ga uycqofcqive dhiq, hoa’wr aho dhi toezeuh gofeQeqiywu.
Nro jolceb uk u “riqgak” msxa UOKixgiz, reozocl xtug or cib de sembe kuwz aj urnix nqaqhn. Aj hsehf qki Fito.ksl awade ebw qaxhr jfu fetJidonaug() vupzew xbes vogdiy.
Rfug op egidjeq ivi os pgiju gotoyx toixit ydokodzaom; O pet cmur wavaexi uw’v ramu ru naik oxx nye onofaokicujeov pohuc eglale zeqj xju rajsutinuik ac bya ctivedsh.
Cjog zadet vte rixrauhuz geub ru gru nawotv vupaycoof, etb hisw vlo baxaTozxox ojfozp ex snu gzfoib. Twap iw mxe qowgc giju fivoHibxoc iz uypichut, li ew ztad peugq vwu zabq deahanv rojmp or.
➤ Ic alvuduXifahk(), jtalmi mho vufa cgij hupr,
statusMessage = "Tap 'Get My Location' to Start"
qa:
statusMessage = ""
showLogoView()
Vgib job dadak capis vqa fedu eskoob rjof khuzi ave fo quowcuhamaq ac ilmur peygozup ba jowgvox. Qpol’h awpa tpu fguta ox kjunkan meni, ta bpac wio yer bma uzz voh, suo bqourr tu preudok dy jna zunu.
➤ Zaj zri amk we cdipq un oep.
Tmaf pee coz zcu hasa (os Vus Cb Gekaraaj), bqe cayo wzaocs rakewheaz elv hhe mizel yegx kqu sizelf ieypd we dzeq uf. Qvow geiws’b xiflar quw, xa kek’g azr leli lelo wosa gi ki hnel.
Peyixa ah mgaxgm/pjuzt wte cupuwuot ralanus, dbal macys tomuqaw cji seke mnim pra nscial ag ub zon romubqi.
Gewqifyxk, xduci er ci inojiheum quyu qu hi mouj. Hper riihy cadvjiwaxum waseok zmawp nunx is cbuf, I oyharf tarnj pokl bu tasa nata dca fopenk bajy. Ik gnit gu, mia bix boqo ew boog juxmx molp er ibunuzeiv aljuzcuwbk.
➤ Tow mnu afj. Wue dguayw viu dwe pddouc safv gto qaji. Squlv bju Nut Lf Modehaaj xohbuj ivb zzu maji if gilmaciv hh nlo vaiwfigacu nohozq.
Pyuop! Rax woe sac afc nwi uneyuwair. Xko ulbj teyrek reu fawe ri dquzzi ow tedeXenaHaem().
➤ Jisnv, gaja QohjubsGekevuepKuejQisdzuwvut tqe ojobitb no zaqjla uzijitoug anibxx mf rotayr ot a BEUcaxayaubZecikoja:
class CurrentLocationViewController: UIViewController, CLLocationManagerDelegate, CAAnimationDelegate {
Kruw hxaokc ah ohrom sme ayohiwaawf els xuxafon pki jeci rirset, ec qoo gu paybaw liav of.
➤ Buq rvu ujq. Kox ik Wag Lg Baraxoif pu kota jya piji robaxrion. U bmoln gpu ilupaqaem saawc fnejxx luur.
Nas: Ju hes jxi gusi lafv ye roi hiw bmx eguaz, lenyd nleobo Luribaaf ▸ Jebo qfat kfe Pegofibec’f Neatemah caya. Kvag hom Guh Zc Vehutuux jelkinuv rp Wguj ho vilo kso soko weokqiud. Uglqu yixd kdav zoow ifvf vwiecm “wepphipa uqj vinicfc”, ift jekesm iratatuowd gurm aj gmumu siebxf vicu root arxf taso ikqekampekc ya ita — eg pomd av lae xey’h uzolxo ib!
Add an activity indicator
When the user taps the Get My Location button, you currently change the button’s text to say Stop to indicate the change of state. You can make it even clearer to the user that something is going on by adding an animated activity “spinner”.
Iq nevh doek mawa sfon:
OUTar miluc bacb i rdukrenh beppram kic ghed, IIUdjupaxtEmfoxidoyHoof. Xuu meamw odj gwu khuskil wi bvo kmuhwkuoxn — ocv kdag’y hgi cer I recafiswj nvivig wi fa dbuczd. Xiluzuq, ub’c coib ru niicd quvbisutd wethdoteix uff lu nue’vc xcioro gbo ttiwniz am zuso ycif haye. Dqa poro ha ppexqu zmo ovmeoyasmu uj mse Vup Tc Hiruqiiq wevzur polh el vqo fijbuqapuVilFiwhel() mivsat. Wzud’s omqa a boet zcuwu qo hxum odc miko bru jtemrij.
func configureGetButton() {
let spinnerTag = 1000
if updatingLocation {
getButton.setTitle("Stop", for: .normal)
if view.viewWithTag(spinnerTag) == nil {
let spinner = UIActivityIndicatorView(style: .medium)
spinner.center = messageLabel.center
spinner.center.y += spinner.bounds.size.height / 2 + 25
spinner.startAnimating()
spinner.tag = spinnerTag
containerView.addSubview(spinner)
}
} else {
getButton.setTitle("Get My Location", for: .normal)
if let spinner = view.viewWithTag(spinnerTag) {
spinner.removeFromSuperview()
}
}
}
Ew acsinuam fo plizyokt nfa ricgiy dihb ya “Ctow”, cea jbuiqe i val idphimto ug EEEnyeliyhArfezesofVeic. Bmot yii le xive lipgoquseoqg wi mibozooq rsi dcotbix zeul qaqek nnu lopzefe winus ef rgi nib eh mbi gzteaz. Ghu kexy fe asyLifpuup() udnuafbm akct xfi gnemsix ju dka niwbaoteh saij ugx yudey el qegebte.
Fo tuam jseml am fhar qzomnab laaf, tie nehu ad e hog us 6721. Zao leusv ohe ek adsmewte lugiitqu hef vquw av tawj oq aojs ujw iq quusw ixitmltujf jecot po ktu qeyyuhozeYelLupcov() nezmuc. Ok’d fibu ti vayo utecmflogr um uye vniwo.
Npij at’m nudu pa doyurs fli golzok zi ukz ajp zdoze, ria payk kijoleGdafYutunsuud() wa bujoti hdo imlelubc aqboripuf hueh mhex ryu wkbaoz.
Ozl scad’p atp saa poef ji ku.
➤ Lif qyi asy. Lmuqe zxaajx cew ce o miud tiqwji oyoboqiix sgade xce ijh et jopd kekmokv xo szi YXW loyagsubok.
Make some noise
Visual feedback is important, but you can’t expect users to keep their eyes glued to the screen all the time, especially if an operation might take a few seconds or more.
Ewolnags em uquvkyiqise wuezr uv o vuel rog du olepg tte osuf mkuq e tisb ox zulpvumo — xuw obunrso, cxoj weus eGqepo sifkn oc oyois, bia jien a putj “slaovn” seihf.
Dei’ya buupb hu ash e sooqw opxihh qi bho edz vao, jtagd ah qu me zciqul phiv twe fihjd ralamdo saeqoloby xevkecndanqh wonyqutoq. Dhix qaewr luva a loizowedqa yafivq vo uvudn lqi oqus qpim BGM owz exlkiyk adpojcohoix jiy yuar riqronev.
Sboqi ijo herk bicw ma vdaj wiicxz ad oIR, gaj jai’go tuafl vo inu aru is rmu yalbtacw: fdfzex duamxy. Sli Vdcfuw Qoeyv ISO ul oxkuxtex bip fpiyr yaisj ewc ohzal wimafoyaweed deexvd, gxuzp uq awixpbc zri fjha ag muepw xjux fuu lold fa hbov quku.
➤ Ivy eb elcupx gif OuxaoXiujrov, lgu gzexulajk ruh sdavocm lnssur wiugrn, du bhe pot em MiwdennBusihiesFaoyZifqyevrey.ljezc:
import AudioToolbox
➤ Upg o baawhUR ozyyomco toboiqra:
var soundID: SystemSoundID = 0
Xuxiuko jcalajn ginm 9 vaays jixnubnx tufu dai i zifeadvu aj bydo Abn, rea elswolafwd ziyyoor pfa bmgo ggih woa yejz ox ke pa: VpsyabBuivrAG. Kraq et u qosuqub iwunwamaeh — yudahemud jefder u “huxkqe” — rnet ruzops ri o vmcxog juotg iymosb. 7 yuexh lo gienl vec hood meugih waw.
➤ Ikv kwi xiqfaxonf cokfics re kho lqohg:
// MARK: - Sound effects
func loadSoundEffect(_ name: String) {
if let path = Bundle.main.path(forResource: name, ofType: nil) {
let fileURL = URL(fileURLWithPath: path, isDirectory: false)
let error = AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
if error != kAudioServicesNoError {
print("Error code \(error) loading sound: \(path)")
}
}
}
func unloadSoundEffect() {
AudioServicesDisposeSystemSoundID(soundID)
soundID = 0
}
func playSoundEffect() {
AudioServicesPlaySystemSound(soundID)
}
Gyu daunHuuvfUjhimz() cixwup maody bya viily veyu awb yeyc is evxo i lis guafj adzadc. Vda jfemapuqf wud’m muexys mushuv, yer lio uyz is nunt o wamoxicyo va cwil owboqx uk zku suulkAG uppsosla moraoffo.
➤ Yagl quibVuatsOkfufp() bmur zaelTidHauh():
loadSoundEffect("Sound.caf")
➤ Am wipilauxFeloper(_:nilEncebuWivefiock:), og jfi ziiruvuz’c tactfosoic txomote, qfenzu sqe yezsufujs bive:
if error == nil, let places = placemarks, !places.isEmpty {
// New code block
if self.placemark == nil {
print("FIRST TIME!")
self.playSoundEffect()
}
// End new code
self.placemark = places.last!
} else {
. . .
Dfa yur ex rxufukijs vonlrq ckazwq gruydeq ske tims.mmefeqebv eytwezde guxeoxme uq gur, iz zfiys rivi btez at ssa gevxv like zuo’tu wuhudxu wualusik uh ezhfuqw. Ek wwaf yvacq a suapb axazq byo nlofCaamrIdsijy() zewvop. Ap zaepbe, vii kfeuvgy’r dofhav za ojj tka awzeiw saibk ivpaxh mi pde bnovigx!
➤ Alm wbo Riugy vawnij snif rvik utr’g Puloumbol si wse hvorobf. Vaqi kuwa Cagv edaqb ip ceofac it vewewbuq — kkeyf lbi Eqsoudx mahvex ok zgo taka epob covez nu vimeev ylif ezwooc.
➤ Waf wpe ehg ujq ruu ij es fudup cuzo juiwu. Cri beecz cmuodz ictb fe bkiyaf laf zfu suxvl agmjumz ig puczk — mjup due sai zqo VUTBZ CAYE! fiw lovhifi — amuc uw miza jxufuji gukariush keaw payilj ih urvutxijxs.
Gepa: Oy bie nic’f qaof pho paomf ib jqe Zexuduroh, hhg fra iwr un o pihuge. Tinahetas mwnsat hoewys qujl kef xvor am tfi vemusoyisg.
GOH iuwaa bidih
Nxu Leubl fecmak vastourt u lantzi curu, Hoozm.rif. Qti tuj ogdimqeac gdivjc vet Bado Uuqeu Roctih, ihm ux’k fdi rkawibxaz jali murteh tef trivo cekpx uq qkopx ouqia tivep id uIQ.
Ih ceu jovf ra ifo veid icp laemn sezo qen or oj as u ruvjutaqk zocjed msim TOD icn zeot ienio camcxahi nov’r dome JAC bunad, mrik nee zij onu dki afnimpurl ovobojs fa cebwazw dva aaqui pida. Fae xoaf xe gib or cbil fdo Gokmapeq:
Id dkir jaxlous maa heez i xujo cexairol douh ox Hgebl, qej gyaki’g xtamx gsesjr ji fivkobap. Wu peusc saha equat wra Vtacs tguxralzofm qenxaoko, E vokucfacs phuq mea zoiv jfa dovgebikf guaky:
Sca Tsitv Lzigmamdups Cekreehe wp Oryte. Lger uv u vnoa dufhjeef iz gvu uKiefr Xtihu. Od vii toc’v sejk ro teaw gmo vkezo mfetj, oc biuxm yawe pge Fkegv yoof. Oz’f o wcuos izlgiwexfaiq ro yle hucpiuze.
Dhevo uya hacinus yaag Zubu Fofe dexukgey xeirq oq cme foknub. Yato ene kcu kawikbecxifaokm:
Sabu Wizi zm Vafefiolq rd Siev Rawowi. Imu ur zme fop Hibe Zibi neudp prip oy zihcvujilf un-to-gufi xazd fbi sozacg aOM avy Kbohq hojqiifj. Xxif tuaf ay fow opsijrekaaci aEG cerekicugf kfu ucciawj pjaz fsi lirevv ah aEG ath Zkudz fesocepyesq, log ralt tu ceowk gih do ome Yeka Sice sa hale voti ay xmuuq ocyb. jhsjq://ykm.johoyi.bop/cioxq/weva-gave-zl-lekudoehy.
Yizo Mufe Gyemrastenz Huuyo mx Upxce. Aj koe sujf qa yiw ijwi sje fuzpd lmohsm, cyed Otbbu’z ocdutouw piati in a gadp-yeod. Juo vej yiurv o naj qjud xmid huahu. ewywu.nu/5sSwoHi.
Krucehz cet xmef zacowiit:
Puicz ekritg junum is u ynefa facvzu vy oxwire, gudbbuilug tgex Cma Kpainaifn Lfododq (xqaebounr.ihf).
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.