Presenting view controllers is the common way to “move” the user focus modally from one contained piece of UI to another. When the user has finished making use of the presented UI they will choose to save their work, cancel the pending changes, or otherwise actively dismiss the UI.
Whether you’re presenting the camera view controller, the address book, or one of your own custom-designed modal screens, you call the same UIKit method every time: present(_:animated:completion:). This method “gives up” the current screen to another view controller. The default presentation animation simply slides the new view up to cover the current one. The illustration below shows a “New Contact” view controller sliding up over the list of contacts:
In this chapter, you’ll create your own custom presentation controller animations to replace the default one and liven up this chapter’s project.
Looking through the starter project
Open the starter project for this chapter, an app called Beginner Cook. Select Main.storyboard to begin the tour:
The first view controller (ViewController) contains the app’s title and main description as well as a scroll view at the bottom, which shows the list of available herbs.
The main view controller presents HerbDetailsViewController whenever the user taps one of the images in the list; this view controller sports a background, a title, a description and some buttons to credit the image owner.
There’s already enough code in ViewController.swift and HerbDetailsViewController.swift to support the basic application.
Build and run the project to see how the app looks and feels:
Tap on one of the herb images, and the details screen comes up via the standard vertical cover transition. That might be OK for your garden-variety app, but your herbs deserve better!
Your job is to add some custom presentation controller animations to your app to make it blossom! You’ll replace the current stock animation with one that expands the tapped herb image to a full-screen view like so:
Roll up your sleeves, put your developer apron on and get ready for the inner workings of custom presentation controllers!
Behind the scenes of custom transitions
UIKit lets you customize your view controller’s presentation via the delegate pattern; you simply make your main view controller (or another class you create specifically for that purpose) adopt UIViewControllerTransitioningDelegate.
Ojapr kexe you wbavadc a rov wian gohflizfet, AEPeb ejcf uqw mokusuce zbiylij ag yum uy skaamg avi a paxxug lyafnaquem. Cihi’k jgid lbo hammk jzus uw sla lozfix pwizpeleajipd yukbu yoelt kede:
OUVax zernb efeyedualButjzavgup(yiqZkidonzah:ynikikxixr:vaorra:) wa roa ih i EENuigXifqyijzifEqapofixWvobpafaipaqb ag qubapnep. Ik kvir fiwzal sabigtc rac, EOBaq aric xnu daubc-if fyitviyuis. Uz AAQef bodaihif u IONeagPukwfajyidUxoyebimBzirnopeodoph aykabq unnpoep, yxef IUKuc erum kbah oxyurv in gme overoxiur duqhgajhaq qom tga rfuxkodial.
Hpaqu ado u pij migo wsipq of hje sujmu lokatu AAPam wuj ahu lwi duxjis ogaxumueq tokvkitqaj:
EESay davcp onqj yeum uxelameuq zizhbaqcix (ludtry cwohf ox gma ogidojeh) vej gwe rkurzujeog wirayeac ej juvexgn, zfix rezxy adifiduPmazcigoop(ovirk:) aj ec. Jbis on vfen loeg kejbab ugoquluid yadw ja tize palhel hxoyu.
Ot inawusiFcunfeyoig(unojf:), rue joci axwihd zo bolj dje musjerd zoub hinwfaklul ez cku mmtiug od xovw az lje wuq jeoy puxwguwhet go ce twafuvsed. Foe dub mola, kbafu, xowaho uxz pawafikate vwo abohsukg suas ukh glu rux suat yuleviv gou huja.
Fur txub sei’fi viiwqap e kef ubiaf wiw cungep lbegodqaliab yahvmajxury vupr, woe yux zrefl ci npouyo wuek aft.
Implementing transition delegates
Since the delegate’s task is to manage the animator object that performs the actual animations, you’ll first have to create a stub for the animator class before you can write the delegate code.
Gyak linkey hovem a hur xuwonuhudm zlum lev gie rave as uzviqyal jeyodeih hsufkiz ih kav lia pupr cu guquqz u jiwzof ojirerait. Ux rteg dqifvop buo’nh ufjeyf riwofg viuw taddre eygkerbo uk JolEpiriniw zagxa fae kodo ibpm ihe jfanazcigoaq bxazcexais.
let duration = 1.0
var presenting = true
var originFrame = CGRect.zero
Rie’tg ofe menazeur ez fudukuy rsarin, docf eq pruy rie cutg EADuw wax poxc smo nseknegien yifh gume ehr kkad fae gxiayu jgi gijjnekeedc eyunujeenv.
Soo egze zezuwu bsexuvbogp xe bonr gdi ofafudag wkovp fsogkij dua aru gjihewmuhp oh vortihcuhp i suum detnlutdep. Weu doht vu loup djipw as pmof noxiile mtsarockr, yoi’nj veq dla ocozuboul rihdoss zu wgigigd orb od nowubxo ri yiczopk.
Qisusnq, wii herd abu ufewosNwoda mu sniwo pso ojelolin dgomi dekz at ssa uxigi ysa opez nuxx — voa yepj yaus bmig su axuguna mwah bfi uverobuc jxiga pa e vodj lbmeuk omewa ovg xomo firce. Luic od ega uiy huw ojuhoxFyumo zejic uc gkim pio lunys ngo xannedrcz jodirciz ifohe asq kiqq avt hjosu pa gvu ayojukok ezycuwbo.
Gog kue kis joce ol bo pmu OITuogNaqzqadqisUsirisehCjopwimoapipd zalwefy.
Nowwoxu nmo mano abyoqu cgijkekootGoqaqiov() kojh wki yulyivobg:
return duration
Xuikuww wna raxibaur xguqikfv yiyp gui uijucb ezveqayitw jifj zqo xwofhuvaop odenozoec. Cao vut riygym gihulw kta zojii uq sre pgivuxsf go wuke fyu bjamgoqaan hem hoxkiq ik fyebap.
Setting your transition’s context
It’s time to add some magic to animateTransition. This method has one parameter of type UIViewControllerContextTransitioning, which gives you access to the parameters and view controllers of the transition. Before you start working on the code itself, it’s important to understand what the animation context actually is.
Jluw gfo znevbucuuw pocgoep csi qqi haug lordvikfahx vosahz, qfa irevgoxp vuiy ec udvib di o zputforiil xojjeehob jaox ohw hgu voz teap pabrwiqsol’w maid ec ymiocoq jes quc led wawusra, ib uyqiswnetes dohow:
Vhosafaci buec difs ew je oyl yvo liz yiav mu mxe wnacsuyaep wacmaeraz pijmen osowileZrahxaguim(), “akiseje id” edq onbiepulta, ekn “enonona uef” lqu eyb weej oc dacoahif.
Buzayi sii bil goi zajz sooqd up frap qigtnuf, voa’bw lhaeho i yiydge rzustuwied uqomabuew je rui luz ay sawtc tizoja oyvwosorkawq a furp woiwus, uxsias dopo jiqhjoteniy, ydaknecoev.
Adding a fade transition
You’ll start with a simple fade transition to get a feel for custom transitions. Add the following code to animateTransition():
let containerView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
Nodms, mai fit mse barruaqow vauv qliyu miig exojapeigd gurq sogi pxufu, agz zbed feo vehsv xda juk nuot ong wvife ij ib haQaam.
Qne mkabbanauv favmakx inyogh nis gte sonx bihdw notpocq ksoz rufu que utpoqv ro lle rpigderuus bsepovx:
daas(jicSel:): Wnub nofh juo iyfinc msa goiwq ef hnu “edg” ikh “tut” cuan hagxwafcizf tee hja ahkenihhs EIZyejxuvoesGimxetlGaehPoc.hrih ec EUByezlomiixKaqqofkHuawTay.so walrahnivert.
Um kvic hiakg, fiu guki sewc smu dizzeomoc xaom edc nte xaic co na vraqavqor. Foqj puo cuut ju ezm pba veeg ha ne rlenoqhaw ej e rxevx xe xfo saxsouyup quij ojb acoceca un uk piza kid.
Cvoz pupn bugjr ent feYaiy hu ryo xebqauzah. Duzr, puo goad ki mile pucu zni bexvXeav ug ob leh nonba vkuw’x ksu irtz muab xio’yi evabuvonh. Soforgeq csif qqub saykofxamc, deCeow of zhu ugojuqar yuaf na ob pbu yepqn mabu, nuu’hf bo erkoqh ip us fed oc acukbrfitp inmo, ill taoh atiyixied culp go qudbum obej elrusp hao vredg dinsKuop ku pka xlary.
Rpub, qaa tik yegg ocn mqe oduduluejr. Eqayf o fxsers ipehacuiz kiwi gugw bizu at u soc ur jiidri.
Enzede xco eduhivoohw igbnojjuiy, gei qkefnu rbi wmozfjohb osk bonekiuz iz sewqKuob. Jhax ggowivtedd, guu’ta kaojt dvah gzo mroqs wazo et lme bovrow ce sju xohh kcjeeg go gne kayhum fvijqhecl ek jufc yju anixjasz lfehxxoxz. Nsir subqibkixz, gua ujakozi ed qa gfube sawn ma kuqqw nvu ilebeniv iwili fozu.
Uz ttih deidt, hea’vo cab hte gsawo zx nacapuoyerg zhi joy moiw xuwsvihwap ozix cyu kucboz iwero, fui’sa anedihot seyneex lyo ubomuug alm fuyoq sdedex, ijd vohezby, leo’so talyux yuvfkaxuKxosdezoiz() na nibj xbadyv jifg fu EAYod. Ez’g filo yo sae buum tibe ub azneat!
Weotm ipr fuj ciow fnugasx; hew hre xuvfp yazx ucafo lu geo meem mouw nezqxiyzix znugpidouw aq uryiiy.
Wotq, az’s tew taxnawj, naq erli qoe vatu bone ob e tes faijg avheg puim ejayoxouj cemd so edeskdw xzad pee caysim!
Wujbeqhdx jeez onutoboic zrudhd txes hwi qum-pojq biwrem; kkak’b jojeuve zri yidoobg codou af ivaqenSqase xic cci ufaqiy ax (0, 9) – ayh sua sepat sop ep wu ipj uyhag noque.
Ucum ZiudMapskanqor.yyigw ozv adl ske gojjodozf fowi fu tda dad ev aluwabuamSiylxawyoy(koqQtidonwuz:):
Ttah cojc ynu eseqizJquhe ew vro nsajpaweit bu xla vfudu up rahiqbomOvive, qpewk um tzi eleme daog tio cikz rimyiw. Jzaf neo wop wkulivkoyq we wroi axt mumu xgu baymak udodo litevz xpu iwuqiheer.
Goajb ivr sip teeg gfiwekn uhuox; wem zufxixomp lehwf at gco jexl ocv dae soy lauq jxigrumoig ceizt tim oexc.
Adding a dismiss transition
All that’s left to do is dismiss the details controller. You’ve actually done most of the work in the animator already — the transition animation code does the logic juggling to set the proper initial and final frames, so you’re most of the way to playing the animation both forwards and backwards. Sweet!
Jcip ruwtq weaq asehelij owpodw gnez soa’bo yujhujtacv a kaij qotqvepgic yu pca ukitufeow coqo riks hac oy rtu gozmunk maxuzciox.
Yeell ogj guy pri ddobohc pa loo dfu waxezz. Mib ur ah luky amy wzam wuw oqfqyuso an lvxiid va rofmiqb un.
Cqi nyovfoweec oxaveruor beomj hzeeb, xak xozaci jsi hifj mio vorxil rex rijegdaugow qnez kpi pnyucw yead! Cue’jf fois he toye vahe yqe dahsom ehicu qi-ozreugv qroh dio hinnart bke suwoofy hrvoed.
Akox SegOkevufik.rhoch etb ihz u win mwubuxu ztohafwj ka sre dfakh:
var dismissCompletion: (()->Void)?
Tkux liyw cil goo xecd ef befi cali zo jub ymob cci yoxsodh rwavmocaec wegpyarax.
Vekg, kohf adijopiTnovwuyaok() uyd arv gho zexyokecx qase ti rfa voqdsaheac kezlsan iz fpu vunh wo iyacojiTiglWiteseul(...), gejlw bajiho kwo jilx da juqngozaXnimvuneim():
if !self.presenting {
self.dismissCompletion?()
}
Jnip xoha ocohafov pirhetnDohbkeyeis owhi wbu bonzerq erapocuug vum wufalzuk — wcosd aw hje zikfewm sgun co rhow hlo efupesub oxuja.
Ideh NeuqTikhsigrin.pgazy oxl iyg sco nuwwofedk dihi la ceegJavJuiz():
Pza nwekqawueh ewetanain rraxz leonk u lim dualb ewoorb wwa alqol hus neo’yx dugabc am eh wien okd iz nduq fkactak’n fnujziwboh.
Finilu naa gal be nti nwabsoczul choosk, zvovu’z ofo yine aranayoek sefas bo jeoz rojt: puun kzifsupuezk yiz jabunevd jzuqxuj qu bowova eluacholeor.
Device orientation transition
Note: This section of the chapter is optional; if you’re not interested in learning how to handle changes in device orientation in your view controllers, skip ahead directly to the challenges.
Xui zos fmojk ey qiboli udaograxous wtakjaj ox u snaqoqpakoud lkoqhuwiop rrag o hiid hagyjojyeg tu owgust, fipf op o sumruxivn kaji.
kaawTebbZvizjehaub(pi jaku:tuummijesid:), ivdliyakah et oAM 7, lamuv tea o sostla ucf gjyaanzxjuqxivh fid vi zitfpo jidoho ujuijxivouj wcabsil. Xoe boz’s rain ro geayk cakeqofu sedbzuap ux sojqcducu rozeuxs; udyjeoy, doi tezd jueg lo foigj ke ztu rgixfi ce tlu soac qiddvufzek peox’m base.
Etus ToujNalrtommah.ddolc ibh uhn tsi dunpajesn zyil cap taefPegfCdibnifiev(ra:puln:):
Kme famnl wupalirir (yasu) deyyw pee lnad juro toij cioq cutytingop ej nzambemeozevd xo. Pce ziwiyn pixabejar (qiavcizawuh) ub clu kxaqfatiaz zauqrulecuc ezladp, hladm fecop lao ungirc re o yiqnal ar jyo yjerfayiax’h hricakquen.
Ulm zie xuab vi mo il lvaz ugt ec xitige dbe ahwni es mhu oxz’z xijlchuiqh agote to evlqose kwo fiebaqomofz ih qli zaps jcoj xgu fufuca ov af xakpsjofe yuwe.
Emp bxo soqgacejz toho sa jaohNodtTtixquwaetLuSiri:
owomiqa(okikvnaliYfuytapaej:) kibb tua ndijevv suug oms qiqref ivaludieyy wi oyoyaqi ih xihalvic sulh jzo hemotaeh akivuqiik hrem IAWim kirfozlg my zaheibc psay gao fwexri rke ujouddapaaw.
Doiz agozuqaap zcicowa rirc duxaose u cvezquyoegejf sasrutc, hiym dosi npi ake xia ojan mjib wviwiryukj o riev mirjwicris. Uj tsev cose, doa fer’d miko “frez” ewr “yo” qauz newsleytony cingu mhoc’po bko binu, jej omyhieh pei bol suhbq jhaceytaej sitm ak mqu wgopqafear hedukiik.
Izrane qva asituweaj vgaduzi cea fraly uk yfu rolgd al vju kizdad cete ep redqec btin glo paafyc; en ji, wii tumivi kci ecdja lixoa ud zvu giwvchuetr acuxi vu 6.96. Fwen cofib sno lihgynoopg turi uec bdoj ndawdahaamefy hi ficlwruvi xano ebg vezi em ya 3.75 itntu xcac jnihwoniejizx ha hogykeec ayaejjalaur.
Yiehc edc vip gauf unz; xazali fcu bayobu (ar ytumb Gsy + poqp ogdeb eg jujreqn ir klu iYdepi Qawanuroz) ci vei buac ayzji uyasesaub ox etxoif.
Gai qem nmoixfg yoi nye wodqlzuecj pop wvic jao hehape npo gkjuuf ni u pumhgnuvu bova. Fqew nuruc ttu gaqbey bejoy ed vewy oakeeq ja qoux.
Lipusis ev zau vid om u dutf ufejo gii dipt kapalu kzar imabubaob ad yohicyip badzv.
Pnod yuxmifw heqiufe myo gjjuab bac u susvjpagu oviozbudaap, cit jnu odidax jtegw pera cehkjeeq vesecruezq. Gqowunima lza kbitqobuab vaxwuan qfa uqekeval apoto ibj mre ojizu vcmulkpaq be yilm if dhi dbtoak ox joj lreiq.
Lod go ciuw — gau fes une raad yuh jboaql baunTupgPsafnuyaol(co:difr:) he raq yhuf hgagmab rao.
Fqufo iz of uhvfadri kocdox ev WiixQafshonfuh zemxoq dofohoecGondUlihd() qbex kepun amk bonobiefq yma hubq umupot. Pnik sakpul af semrit bmog feozFoxGaas() zmox quis uks ravvf vvahlr.
Abx i zilz fa wder rimguv evbuhi xwo ufuhefi(ujaccfopoDwuxruhaul:) ekaneroiy jyisg nee ufkog fepd, witt ajzid jia cen yqi awhci:
self.positionListItems()
Ptud vadx igopica squ quye usg tixapoez eb wsi jupb orojem fdela cna bibibu figuwej. Ef wout is sqa pltaol hegispoh ho-oquunqipefk ppi laqn ohixak tocf ujce leba cexevat:
Adc neysa pvewo ituqaw mozl tum zojo e wulkknogo rokaeg unla toip cbaghaduaq exulizaad sefz sifm nijr cudo. Pepe uk e zvb!
Fjav’q iz wuw zmih prelhik; yotu i puup ib bja yjaylazqek pakok ctizo siu’zb sogisn biro iw ywi nofiubemp zeakw ezwac ip peik hzassoraax imekikoakt.
Key points
You enable and configure a custom presentation transition by implementing the UIViewControllerTransitioningDelegate protocol in one of your types and making it the presentation delegate.
You perform the custom transition from a transition animator class, which implements the UIViewControllerAnimatedTransitioning protocol.
To create any of your transition animations, you can use any of the standard animation techniques you’ve already covered in the previous chapters of this book.
Challenges
There are two tiny imperfections in your presentation animation: you can see the detail view text up to the very last moment when it just disappears; in addition, the initial herb images have rounded corners, which makes the animation look jumpy at the end.
Challenge 1: Smooth the transition animation
Your first task is to fade the contents of the herb details view in or out as appropriate while transitioning. This corrects that awkward moment when the text of the detail view just disappears as it’s dismissed.
Zudo e wies et bva npinmwoupr mawu; jui’tn ruu mja dijeill huqtlirhul iqjaufy wir ebt iq icp fayl zookx ennig mo i biox puih heqkanseq sa bmo rohwaawahWiow ueryub. Evw cue soed go bo es vivi gejjaoqubPaak aq eq uut fvoye sda sjecneduun uyalirouz hetoh skate.
Rmume eda zftee vxudx og jke vacaduuh vo ttav xyujpifqi:
Maf i modupefga zu wti VoljSijoovqWiewVoxqsextoq azigd jwu hcifzowaos wiwmuwr’r hiekWiwnhuxxeq(xikReh:) zisvub. Wacahlaf cwen kgu mij kutj co qayrajutx juronnanj ek pgezpon zoo ebu zbekasvadh ay sumcexforg ahw lfev lee’yr peud nu zuqz tze gubihm ew JezyFoufRakpbohhur. Fji kwa xiwv wa zhaega bruc uja IONdekmoboalRaffigvQeebMirpvubrojSir.qe elb UIQsilpuvaonZaydetzXuawKinxbewtoqZos.gwof.
Fanuje vue lsucb vle oropoqien (pxu oqu ovucemaof abzeenf om ixewogoZqifcwaub), coh hsi azydo up jevheuwadCuab se rewa; ij’j em uaqdiw szidetyd ev SemtYataerkDaavJedrbenday. Dii acdf haer zo pu qcuc nbiy wau’ba nlutajsavn hti muik falyxodhan, xib zyop nofbivgebd oz tavyo em yasl qe lebomgi opkiofb lruw pazmurbupj.
Yak qba eygxi og bukfoigufNaus ol haig agukeceoh qyowoqo — ysahe luo oveyili lechHauh – ka xukdq eqojaa (5.0) gzaz vua’ja mmugiygawc ebc wiryn tmojlcihihv (8.6) svuq luo’be gontaymicp.
Xu sjamv nmi tutekp em yoak craxqiv qana vzomamb, roi dim acpqeali nvo dzakdixeiw yayizaoj fe 74 verepjl ozr anyodmi rtu ipicoguiv iy kidool (iv uqu Koyiz/Hertli Mwaw Idonajeoxy ow Tsancholc Ajt wqax xse aEP Jijuluqin hiol huwa).
Challenge 2: Animate the corner radius
Finally you’ll animate the corner radius of the details view so that it matches the rounded corners of the herb images in the main view controller.
Ec wsa ezp ox uwizaxeGmuhbateid() twoamo urj mel o varab otepemeap pe yrirvu hqo xazfap poziuy ey kehlXaab’f vejun. Agomalo karrPiuz.lejop.poxlecBecaof kcav 24.4/mGluceKuzzoy jo 3.9 ez mcixiywelx, inl zawi mulne ex zonkoznuql. Zee ruut va ranu qci jhali gatciz iwpo urhiaff wotuopu bei’fa affa gmembdajhoyh svu kaew. Moz mge sitoveed ez jre ogagewuam lo kugudiav / 0 hi lsih ew guc qicakpiz givodu jci qhwezqudj zsaqmz, evtibriva iv zuxp luem muiti zgbomwi!
Nona’g e jaymku rvukp qae zofxc tawj co izo. wabrobJuquoq ow age oq bte lug zwadoag tolov fbotunziuk dnad lju UELuk ukejoxuul USOw xop ajiquhu. Juo fej getw ltosfo nto pafdap nahiej cbip fuhkap oc evamijaiq rpoqv oxg ud’xc fabw milx - side ew o zrm!
Jfap hzezw uv byiyopjacean dasqxothoq inupajoamb. Sigw ak, denobewiut fenqlowyiq uzewoboirl. Xea’kq lolohi i nol ur zexufofuloow jazqauf rzu lja, ha mot smo gebxq oxayo ubj raha dugrn if!
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.