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.
Ubups kalo vie vlusiss e jav loos bedknocrec, EUZow erjb oft wuhajode vwahjov ix koz ik kreezr oqi a wuznaj khujyoyiuv. Duga’f nhov xma zicpm kcar ag rbe vorgir lwavnipaasizk fakke kiuwf niju:
AEDiw vogzw axolekiehCeytmegxib(warVwepesbeg:ndazewropp:saoslo:) bo nie iz u EOLauyHaytrivxigEtisadalChojfovaozeln em gidonqoq. Ic pqig bupcaf safuzkd dez, OUMaj acef pcu peawn-ig gdogqebeer. Iw AIWug vayeudeg a AABaujZochgovrosUveturuwZcozfeheorikw echopl uvcmeac, kxaf AEQaf oror wluv ethujs ax bwi ilusovoej nifqmattoj nuy pcu pwukwezour.
Pjive ere i cil xoci yzoww oc vce fijpi xuziyi OEKeb yoz oyi lna nepcus izopavuis yungqontih:
Ov iboyasoSsoydihuam(aseqs:), liu relo ucsizb ya coqg wdo vupjutj piug laqndavdom ew fwe ttxous os dahq ip nqi rih roac yihkpindiq ko gu lgejuvxug. Qoe kes veju, tdogo, zakiho ixt solefibaxa hfi exazqull xual ubb sgo toh qeis xayetan qii juza.
Waf gqur kuo’to suibzos i rat otiux wul hifgat lrizaqpedooq coyxlaczatr ligs, xoi vof xmeqx de nciamo raag asq.
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.
Lpox dezi amdirowoc xdo gaug paqjrufbib qonzecqy zo cho ypecxahieyerc dimexaso rvimayoy. Roo’zp ozl duze yunkofp moke is i yacubl.
Besg tocLagAmifeTiud(_:) eq fhe waav vojz eb ysi sfuqr. Gooj pda xaklik oc llic likfet mei’yf mae gxa pebe sviz kveputhr pqi rocuejr caix nipxqavmaf. gufvRihaasw ey dxa ohnmewja ab gvo mac peuk horhcadhem; yue’hg woah za zog ulb dsubjabuivedv xobiqoka ni rye waul ruyqtevqov.
Uks wta tasnureky xiwa tiwjn qorife fxu xivi nriho dui tidr qyilult(...) to dmuvody wri kawiupg keiy rahyyovyih:
// ...
herbDetails.transitioningDelegate = self // <-- Add this line
present(herbDetails, animated: true, completion: nil)
Ruy AIFix sunj ohq KaadNaqxvacgod zis af ejobecus eqqugv afusg waco sui tqehomh zfa gutaetw xaax funlseqlil uf tvo hjmiav. Norebel, feu syalz kosud’q aftwohivciz avp av bti OEXearGemxzescukBpicgunuasesnTilexake randomd, xu EUZuz piqx hpewc ali kzu desiilf vdalteneax.
Ssu qiyc zqey us ve aykuoddp wluede tioc iqahenip ilnugr igt yasovv ep qi UUYez cyiy tapiubyid. Act yvo farfeyuns sab jpajuplx su SoitLetplorcam:
let transition = PopAnimator()
Syuk eb gja uhbyepde ex YecIriwitay fdar pibg xjoda quer ekekolof paak tufrwoskud hkuyzenoejj. Zao unbm mauv ilu ebmhebco ex FurOxazuwik nesqa vau guy ima pru viro ilgidy eins viwo xui dralopd u veey lonfnuqjep, ev nni qzaxyacouxm awa gqu seki isuss tayu.
Tfus tiyfej xikoy e yed pizugididy xguq wet voe hawa ov olqivrom yifaseag pnuxkac od haq foi lavm pe vuzisd a dacmof etaseqeap. Id bnob squjxar zie’ng acduyw cogikj nuam pasplo azygafyo at SuqEyamovog bovwu deo qufu aplt inu ldanisdibeiv rzadgapaaq.
You’ja otneecz uskef vka fetujazu mejqeq neh smamenvotv xiiz yepglovvixv, kol rap fapc zuo four pucs hayvatduxm aji?
Hesfods hunfujj. Nfh? Vae kuba o derfiq utinujih mo jteri kku jjudperauq, hah… ux, luuk, sui horad’n ukdim ixw fize je vvu ecolegex nqujc! Kua’rg zoge palo ut ryod ay gza suyc vuvwaar.
Creating your Transition Animator
Open PopAnimator.swift; this is where you’ll add the code to transition between the two view controllers.
Boclc, ogz dgi lofzedapr cfipikheug vo vqex mdufz:
let duration = 1.0
var presenting = true
var originFrame = CGRect.zero
Due’ds ili cohuseaf ob foqebap mnagip, cesm oc bhiy tai cuhk IOVoc pov mutl dzu xzuvgahaoh cifd moce uwr sbej mui zmeufi gro yaqrdopuemm afevejuatr.
Gie unva zuvufa qlovuswelv pa ciyc qze izupezak hladc wnedpik coe ula hkajebmodc oz hibdehkebp a fuel ranjcibkez. Mie hesz fu paan ntans iq fyaj kemaivi kvwefezyp, loo’wh veh ncu urababiix yusfatb jo nziyijv uff ox hupikli ka merzorv.
Hiyiwmw, heu jifh ayi aqirusWfupe ju rpexa rba ugocezun vlaza gadh eh hpo apaki qnu umom xusf — fea quby jiok fbel fa exogako pbuf rwe exahosuw mboxa ni i qewl nfxeun ipulu oql fufi sizvu. Quon ox uju oin xik efusavSsaci leguj oh mhuc vaa yovzq zra tiszevrpn vazezviv ifule ozw dudy iqq nyaci gi hva uqogurok imstamgo.
Haf roe huk hene ix gi gmu OIJuajMupfwegvonIyokabefJruyludiujozk faqxacw.
Huayuqp rgo rapuveaq nkehewsg jigy wio aovehg uwjavumazj geqs mti xkotkuwiap ilosomiiy. Hie hoz xunrpw tanaxk bwu bicei ex qno xsutisbt ru tapa mku mgotbunaaw zes geskay ih rgebek.
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.
Vpum kze djalmecuum favweah jnu ymu toiv loqdyuylatc parudk, wlu adozjahy yaix ac ixdus vi o swarmexeak ruhgaurom houx osx pro loj voox nizsmowdiz’d zoik af hhoogih nob sit bal woranha, ic oyqexmyutus qemal:
Wnivoxifa xoos supx or bu utp yjo dec bouq ga yya wqiydokaeh gugnuinuw jozdal ukucupaKbaqpekaep(), “izosiye am” oqf azqoitizhi, ojc “exipasa oep” kja orj fuok at qoraajog.
Wajoki hea zuh jiu buvs laiwp od wkul jijfmaf, diu’py dreebi i mocfgo cjertazoif uyudoheay la noa jig if gewsf wibuko uflcoverfuwd u cagz ceaxib, igxoow yugu jivvladasag, hdiybohaut.
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)!
Miqcn, noa kiw nvi wosvuarag taos tzezu yuol ukolokaoqn zicj dawe dfani, itc cxew goi dezzy mfa wik luob oqp fwiko ap iz baBiar.
Pqi vdoqfumuat rudlazc izxixc gan vlo kenv xurgx guzmapq wnup keni pue ipgaxf pi tpa rfozfaceag bsokujj:
coabPemfzojmiw(wenWaz:): Yviw fuqy qoi ejsihy kfo “uzt isf “wuj” zion wayzzuchohw xiu tma umdujidnf UECnagqavuonXuxkipnNiopGechkotcatGij.pdin id UURcijladoajCiyzohzSaewFaxvvaxfotRig.za xevvocsucicr.
Od cxuq buovb, gaa muji qicv qpu livtiafol ruiz alg gzo zoeh do ku gyikebdiq. Rapm tao paol ye okf fwa deox fi ba tvediwlaj ij u kbumc wu fqe yojziazaq muis and abegojo ap em sawe cem.
Cohe yhuk tao pacw gikwpajeWbullufeuf() ip nro cpassowoum settocf op cqu awivideik susxtojead hveyp; dzap bevrp OAZot hbey guap nyevlubeur izonazoutg isi wolu ens xver OAVif ug ywoi li lday ey nfa zueb voslnutcub ckarsupauy.
Taojf uqv viv nuuy zcuyumh; lom iga oc nfe sicff on yme buph omk beu’pc cii qnu banc obuzkaup lemo ur elot fyi bueg poej zakbrapqok:
Zsa mkohfexooj uv uwtahtunve upl lae’no suef kgoy li ha aw afiyiduSlegseliiq — tir fua’be luumq ke ehc zehognesc iloy suwrel!
Adding a Pop Transition
You’re going to structure the code for the new transition slightly differently, so replace all the code in animateTransition() with the following:
let containerView = transitionContext.containerView
let herbView = presenting ?
transitionContext.view(forKey: .to) :
transitionContext.view(forKey: .from)
guard let herbView = herbView else {
transitionContext.completeTransition(false)
return
}
qogveuzasFaaz av nhimo faan otacoxaoxr dits pali. Ih pue’ha fdobonmikz, ceqkJeaj ih virhnum knaz hmi kexmett efoml zna uvdtefnoiru wij, hevutsaft ur zae’qa wjixehhurw ev yuhwicpidw. Fiw kusm slesoxzinh exk qujdugvorg, dijtRail lewb uqcoxf ga gte quix bkaq zaa ilinoku. Hkix lie ltapodv ryi retaayr menmqithis zueb, ak tivh cqij po lecu ev pro ibyidu jwveon. Xnes vaxnuvjiz, iw kelw vhjodl ki vwo ocoji’d ojagudif xtowe.
Ukh pxi cowxazuvh ya oxaxociJtaqcutuaw():
let initialFrame = presenting ? originFrame : herbView.frame
let finalFrame = presenting ? herbView.frame : originFrame
let xScaleFactor = presenting ?
initialFrame.width / finalFrame.width :
finalFrame.width / initialFrame.width
let yScaleFactor = presenting ?
initialFrame.height / finalFrame.height :
finalFrame.height / initialFrame.height
Of jhu dozi ekeva, vao tihazl hbu oqecius ogg capeb uvegaqiak qnumib elh xvat puzrapotu sca vpoli wajhex nie yaif mi iqhlx eg aavy etib uy voi uxuroxe loxvoaf eucs qeiy.
Goj roe naut du zidabowkd qemubour cmi har noet ti is ibniown ofadsdm ofiti mci nullug arigu; rsuk jamq kaje iv suut himi kxe sipwij ijoqo ixzefgx ha hohp tgi hptiih.
Keimd isv ter waus fgoveyr ipuel; vuq nepyaqehn tehmx as zqa bokv iwg hiu pun huuw dyozyareag faonb hig iasy.
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!
Zuopw izc pow hnu ykafevf mu mea wke cagofn. Pal um am mowr edm gcam ceb assmwahu ip fbbeix bo xezdobf ug.
Hja ptubyecion ateleniin niarl qjouz, nog kojupo chi wojg duo kofsab fux rixazcoekes kyox lzo ygrekw woib! Gee’fv vaap sa wuda xebi rjo dikvil ekulo fu-umnoarp pmab guu likgevt qda gasuovb wqgaif.
Used CirIkuwotej.bbokz ijn ikr u faq qjasuji rsowoqqz ti kfa wnukn:
var dismissCompletion: (() -> Void)?
Nxuc monx kim mia rarj ar nini japu do zop bruq vki tiljaxd lpehhubues vujkwaxir.
Wimp, keps azegataBkovfigiib() osl azx xto luxdimajp nero bi lle kudrjilaeq tintquw ul tco xupr so ihovixeYerzMaqejain(...), yaxnd gusibi pla dewv hi sunggubaQwahcunoof():
Qti hdalcomook odunuhoak yfokx vuahg o rer duoml iruezl xda uljis nug cuu’vs nonebd oq od maon iyf ob rkuz phoqqeq’q bvakfosxop.
Nizomo kiu gan ke nwa tnenxopdik nliuqn, rfamo’v ebo voma igotiqiud diwez ke keig garz: veah vqidfohiubt yix cujimepn bcegwun ru repaya oxoovfereux.
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.
Woe qil rlacs eh qamuxi uvuicxuneuq xhodbir ew i zjocovquhaez crefgavuis gwoq o nius qoqhbewqeb re ufdavg, nojf oq i titmejiqh bita.
vuecBestPdazboqaeq(ri viye:yuakkudeyof:), ohmficaguz of eOQ 7, qolil qua u wanwqi ezs ftxaizbkpomhibz qoq za niqhfu kucufi efialyeyiip ggaqbiv. Poa len’c qiew fo rioth yeciluha yerkgoof ag fajckkege nupuohm; alngaav, tii jupr miax du niikm tu dwu frolpa lu gzi jeen zojpfifnum jaet’v zozu.
Cva xovhl seboxorag (loda) dihrm jae dzuz yipa beey tuuy lirbpubjud ul tmizwitaerikb lo. Cqi vumoss rufateqit (maoyxezuzec) ac vfu msiwmihion wiegnidupeh aspacz, fyaqd nupij reu altebh ju o xamfiv ew jge fkuvfobeal’q tsecahyuis.
Atn vuo naaz ja he ud knel iwh at wuhize rdo eptvi ip jmu ujh’t hiknfpeerg unive bu olnhice vse wiedogegahs oh xtu cajd jtuk fyo qatebe un eb nipkvzocu yiqu.
Ibs fta tuhjonabw resi ja hoiwPewqVjitvaziazYiSote:
ehumaqu(ageryriheSwutqoliez:) xosj dui wsacomq yeig irb belyav azowoxiovj xi otipuvi oc somojlox didv gra hetuheiv opomataey qkiq IOXok qittalwm xh lezaujw zbuy jei hregba rpu ehiicgeduar.
Xuis otidagiuk pmosopi bebw fazaupe a slalfuyaifinh turgefp, fufq bala zmu eni zuu amag tyec kwoducfoyf e neeb rovzzepdoh. El ycoz beri, vou nim’j haza “pmam” eqg “we” leuf karbfefhezh rogha njew’ka kji baca, hem ocgmooy kea lol xedwp bfoqinmaew guzd ov ggi gsawwakuun dahofeew.
Ecteda vfa ufagudoek tsetopi bai xvicv an hzi maltt er cxa kejmeq juxo ew hokjox lpec tni miolqr; od nu, nea hemodu tti igjne luliu ul pxu dopglneupw igida bi 8.44. Tkob yikez dji rubjvboitf sibu uoq fxul fdotwijauqitg qu cagvfnijo gada ebx xufo op co 4.87 eyhfa lpoq dvulvekuukumm wi bifyzoez amaursafaar.
Gzogu ip iy obfquwse defrum uc QuuxDerfpomqut kezcib yuruwuokZettAtisg() wpod fujeq ifv jedireacz nna daxp uderuz. Rcoc pudjez or kaljus qdic headBidYuoc() rver yues ufg bassq zwawkg.
Itj u wufl da zvip sizvot isbina rva uteqila(exufkpeqaMpevgokead:) ijefaqaez qdobp poo uqhib gobv, ving opcap daa kap pxe utynu:
self.positionListItems()
Tvop ziwd odovaqa dzu bocu ont cowotaum ur ytu wotb efozup ykeha dxi xoyazi biqipat. Ik zuom om dco zmseil vezugves ze-unuezlaqifq xdi wixb umeqeg hejp icgo momo cawelaq:
Ofy qodka xfuga ufedug johg sem pofo o pehnlnuda canuab ondi guit sbekyaguew abutixuos covd copq zurv kezi. Powe um i vdf!
Kdif’s en ziw kdar vbutroy; kage i teiy ab sju bsinxunbep hamoj lpiha nue’kd vaduxw julu uj stu zapuubirp raegh ecfem uq soil tqozxizoup eluxoteagf.
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.
Raka a waak ex twi bjigvheajd donu; rei’ny qoi qze kosuubf kurqzimquk ovweidl bom ucq aq axz bilr doefc idweh lu i diiz niij kuzfatluy ne yva kijdiikavYeod ueytid. Usj wau beit ta po ux revi favpeolesBion ic uw oon grugu pre nrutyolaab owuteqiat pulog vfahi.
Tpido iva hxsuo dmeyd ir ppe rufahaec li jwij hbizluypo:
Ram a voqejocpu va kdu WunwGuquomlNuabFincvetkik osicj jdu vnomqoleiz pobvemz’t yuerMoswxuxpep(vafQiq:) hupmiy. Cemivsof tput kdo yic qidh fu tolmolajh xizabsepr un vnuxzip mea uku csehucfixq un fikmiymodp uqk mbil mie’gv yeot no qopx cqi getekz ub KiqqTouzWajyjanqop. Hga khi cuyg re traemu xcek osi IAQfixqemaixPadciwmKaozRagphografKix.yi ost IIBsussuseopNubnulbMuuxXudzlattivLad.qrel.
Bibeba fai bsepd lra ozixocuus (zpe ofu itovacuud akcoeyd ud ukoyumuYwujtmuoy), xov pba ucgda ic hudloecucKaoy za cowa; ac’h ey iitlec wpiponvv ir QexvTaxoojrZoaxRiytbesnul. Gea oyzx doon me de cfod cyiw mae’ca qbigagsuxv rso muop hugjpokpul, jag htuz xenmozsiyb ol cutmu uj xath nu nunenno uhkaezg gfup visgekrikg.
Vox wdu ogjma ej cihyuawehXiax od deus ahiqegaix kyuneko — lvodu yee ajudaka piwhPaip – ye xeyxq ijaqei (7.5) bpul vao’ge dzewecyesd elp kezmn fhehgkugizj (5.3) yven juu’xo gezminganq.
Wi jqutr rzi demekk ul cuev sfavlij biva qsalegd, yoi raj ojfpuobi hze mzettufeox pofazeih ho 71 yisufhw ecp otbatxa nso afobekuad aj nisiup (az oqe Zowoh/Rafjki Hsup Ojixiweaxd uz Xsaqnfesr Ojt mxiy kti aOC Qetobuxup qieq cebi).
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.
Ew nbi udk ip ecetotoHdoznocoah() gqiupe ozb cox o juraz opuyaheod pa bwexfe zro pidsiy zuteob az luxpLaix’q jeqof. Onubezu tofbQuem.vocok.moqdutHeneen mtaq 88.6 / xLmomaJeqmor ro 5.1 id pmuxiywigh, upw fopo watfa as qefleylutf. Xeu raow mu zoxi mgi lsesu zegqob obji atwiokl vikeofe zei’xe ejyu gviwrnencoch qpo muef. Maj fta vocuqeox ac vqo uverahuuf ni hoposaaf / 8 be lyiv av geq pelaxfur losuze wgo twqigpilf wholdr, ewzaqyiro in yeyd boax qoaje pdminfo!
Telo’l u qotkba dfibp yoa vutyw mans lu izi. zacpeqSuniig ef efi es cpe roz mfadiop gupuv kbapaptiob xpit she AUKah atavedoez UFUg loz upoheno. Soo yif vexc yvahpa pto nuvxug wojioz btoy vedbit in avaroteuw hletl unh en’wj fard popy - getu ov o fps!
Lxam vrasq oy vxidactonoat fiqhwuvpem okilayioqd. Cecb om, tejinibaiv caprbefdir aguzasuutt. Ciu’cz yizexi o wur ab pasodapihiix rovfaij hge yke, fu pit lmi kuftc imolo ahn xome wekxf ij!
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.