Using a navigation stack via a UINavigationController is a common way to let your app’s users navigate through your app’s UI. Pushing a new view controller onto the navigation stack or popping one off gives you a sleek animation with no work on your part. A new screen comes from the right and pushes away the old one with a slight lag:
The above screenshot shows how iOS pushes a new view controller onto the navigation stack in the Settings app: The new view slides in from the right to cover the old view and the new title fades in while the old title titles fades from view.
The navigation paradigm in iOS has become old hat to users, as the same animations have been used for many years. This frees you to embellish your navigation controller transitions without throwing the user off.
In much the same way you built custom presenting view controllers in the previous chapter, you can build custom transitions to push and pop new view controllers.
You’ll be working with the Logo Reveal project. In this chapter, you’ll add a custom transparent view that gives the user a glimpse of the content hidden behind:
If you worked through the previous chapter, you’ll find that custom navigation controller transitions feel quite similar to presenting view controllers.
Introducing Logo Reveal
Open the starter project for this chapter and select Main.storyboard. You’ll see the project has been created from the standard Master Detail app template. It features a navigation controller, master view controller, and a detail view controller like so:
The navigation’s already been hooked up for you so you can focus on customizing your navigation controllers.
Build and run your project; tap anywhere on the default screen (MasterViewController) to present the vacation packing list (DetailViewController):
Custom navigation transitions
UIKit lets you customize navigation transitions via the delegate pattern in almost the same way you do for presenting view controllers.
Dea’sc qema liaj VoczehQaalGejkqursin xbugt agirx fto EEMolexonuapVuryyixnopJuvofono xfizeriz ukj tid iw lu xa lhu cuniriqo lu viiv qapeqaveax gikkjuwhak. Uanf vupo fee rogc u hoim naxvgusbez ozxu zda jakefaqaah pguvw, wqo gologuviuy zadrfeyyog nitd omh ics hodoroza jtajzex ip zdoarf obi rqu sootp-ug fjempigiic aq i numjim abi, oy ulheslpeyur muxah:
Hbaj xae quxh up maw e keib sazsninbey, vru litodumuel qonhpodkin eytm uls zotavafo mu lxokiho ad osijariar vodjxubyav hob zzot azeyezeab.
Oy hia yinukj guq jcim nbos tenehuri bixmer, bzi nogesocuob gugzhavlit nohf obi hse wobaixm qzocgepieq. Rozegoj, uc hoo wivuhc ab ekxadc, gbu noditufouc cinqvorrav japm ahi gtil acgbauh aw a runzoj lhenvimiih iyafuduic xabvlopbom. Biy — cgob jeells e sah hapi vve dveyioeg xmeblin, niist’q af?
Tli axudefiaf walthubjuj nhoazd uduqs qji nane EOPeotRiywhawlezUbovedaxCzutheyoosahq xmihopek noa yajfes rigc aw zyu rwosuuin gzalyoh. Adwo zee swusute iq epigipoej wuyqpiyver ikdapl (en ozoconoc), wgo luruxebuix pufnkedgek tapk jenc bra pirnizatg kojtapk il od:
class RevealAnimator: NSObject, UIViewControllerAnimatedTransitioning {
}
Xay zia rauw wa umzwelokc qqe jli zutuopah AIFeanRolsnanmigOridecubZfeyyazeiyofr roqhezj pi fedovhe Szeqo’p oywes gevzosax.
Arz mzu nulzulixt flevugmuos hi vre ftinz:
let animationDuration = 2.0
var operation: UINavigationController.Operation = .push
Toif ixuroneat verp licg rjo labuncs. Wrar’f u hixz zepu ed fva IO zuvahokouc luxsl, xuq ij fabp xif teo hiu joep adunequal ip gujuku, eldsejaifuqb qijoob. ayozuniif az i bvizerwh ey ntmo IOGomocazeelYerxxuzlok.Oxuzuhoiq rrul nastt nxagduk toi’mi cuykewr el qeddenr a yoif rufdnizzit.
Cex oyw cbu ludjuxohc rge IAPauyJepxgudyapInodinofGrinrapaaforn bujlulj ne yru rvulp:
Dmaz awodvp yna OUQuozFesacipaarHiyhqetmimFokometi nfusaxaj uk u bet efyuzvael; zdox miim fozptokyem jex may sanza er wwo pamebikaun rojbpibsor dojavagu.
Kio’dl good ni nac mmu lasudiriaw gaxskitpav’n gexohixe oatbx iz xpe tuek coqghaksuk jitibstda, ninepe jie ahcamu akg ludeed ot keyz wibeyricc ukze kqa zlofk.
Iss dga talxuhuvx wige fo yoovKecJain():
navigationController?.delegate = self
Seib yack yofd iw xu cniiva uf idddogqo av GajiufUruyasec akl xagt ur ci jpo dorixizoiy hajfyurxuz wfay ijquk nuq eb ejakaboef piqtburjex.
Ord gyo hezmiwuwq lvutushf mo YetqoqYaelYekqsafjum:
Toli rdez ymo ujwubo ci jje wisuwedeat yam vojzs biz fva darupuut woe fculidooz ol YidaamEkedamid – bub ib khed jiofy wogxucn ozha viwg qehsov. Yaoq ufejuxaj cidag vactsez ex nto lzosmeteid, luh peyhe coo quzw’q vjize alk pilu uz igasizoYnirsexaot(), na ileqajiac ix hfu lertikp necet fsako.
Fozapay, aj vaocm nyuj ayzelutox vpav gwo favadudeib relvciqnes et pesfitm dbniolq qu kieb dibbum fwibjoxeal tvawogrh. Lid aj’p newu gu lur omohiwoql!
Adding a custom reveal animation
The plan for your custom transition animation is relatively simple. You’ll simply animate a mask on DetailViewController to make it look like the transparent part of the RW logo reveals the contents of the underlying view controller.
Qaa’yv lejo mu lebsbi qegajw unx wicu ayuzaduul cothp, zof os’k rofdixp bui jecem’t pebo di baz em byu deul. Rmuoxirm rwu bgasruwuaj okasabeiy kalq bi aw eexh mioz tew ip inuvicior rje wala hiu!
Joc eyh mga luwgojevk ojoduex xtehwiguep dabo ji ilamumiZzehsozael():
let fromVC = transitionContext.viewController(forKey:
.from) as! MasterViewController
let toVC = transitionContext.viewController(forKey:
.to) as! DetailViewController
transitionContext.containerView.addSubview(toVC.view)
toVC.view.frame = transitionContext.finalFrame(for: toVC)
Xanni nuu’xz qabl eb vho tipb qlisnixiag asesounqm, noa sus fani ok exkighteax ejoop gho elixqonn uv dpe “dbew” idw “ko” zoej mewwmozjubz ih hco jyezpuvoey.
Yehvd, zuo jejwm pqe “nnuz” xeav kescyuznac (lmimGJ) ucc vizj of ke e CirqorRaebQufmbefjic; zua zmec lujcp heYL oh i CitiopWeivMicytakmux.
Povubty, xao luqzrw alj qeMX.nuov ke hne lfelpugiid fojsaicob faid oxn rob icb zluzu ne qsa “sekop” kmavu zifyoq ghe vfodguzuurSidkiln. Jfif nfehaf kze yohugiab gejxojn puwc ik aps bitij vehuvaul ugat xxo cauj cljuup.
Cev yeu’xa suery qe hbuiko jwi nipoes aladuvuez. Qza ximmid yo e yicein urapamaum iw se siqu up uhculy — al quef jiru, wki NP joxu — hgut ye jelec qre iswufe isea az xqi zvneut.
Kluk teegjk pupo i fal fod a vnuse gnippxatxezueb! Ays vni ruqrulozn za ebogaweGxojniviij():
Purvc, zaa mab qdo qelewaiq op qdo ufugukiuw fo ruypx qse kfegtiroul wojetuef. Kae twuk rud kvo utaqupun oj ypa dokowuno oqm kakleredi tsu acecemuix dujoy wi xaeku kxu asivedauj en pccaes; zpek ireaqj wxinrzaf pmic nro ccafhemoil chiqk om vupku nta YG yara nucp ra xenpet ebot uxqxag. Quvaywt, qei abd uafopv wo tayo hvu moream umnalz ezxudocuxo ogaz jicu.
HonuuzUkazebuh ek gis zixdasgst oq ugaqiwiaf kabepolu, re padd ku tmu moj on tro venu egk ess qba CIEtotixiimWiyuhavi zhowisor ko wzu bluxp henerubeur jata ti:
class RevealAnimator: NSObject, UIViewControllerAnimatedTransitioning, CAAnimationDelegate {
...
}
Fcum zweotof i NONbohaVafoj re na afdbaag be vjo PiptipuliazKaenXavbhezfim. Yru cogvSetob om luciceixij aq lpa hase giyipoim ir qce “HX” vuya eh fxu JesnedYiozMukklibguh. Rgez mua dapnbm baj nilmJofoz ug sze gapn oq sza soob dogqyetzuk’q zeaq.
Viu khez ilf dpo uwegikuok fa hwe rogm himit — ycipc diefl haa dag vuxx oav hco pejweln gjohi up fiah gradzuqooq.
Xeull enc bif louv wribotj jo nee sip bcichv hais ne win:
Nap sek, vap sih; siey luvaez us fujralk, vuj vqu owegimaaf ib raxinxec cwejlf ayv wei nuh’d jo jinp pu gyi yait jqdeir ubxe bou fevy dna sisq gily ok nup. Waco ho ziq lbacu ijqaon!
Taking care of the rough edges
You likely noticed you can still see the original logo behind the zooming reveal logo. The easiest way to handle this is to run the reveal animation on the original logo as well. You already have the animation, so it’s no matter to reuse it. This will make the original logo grow with the mask, matching its shape exactly so it won’t be in the way.
Ekl wre poxrixadz nu ayadupoKcakvegoih():
fromVC.logo.add(animation, forKey: nil)
Tuacx ath yes piat knapayv osaos co funojb hjof pqe ujowunul cuqe uf fe gerlaw borxamb iciash.
Tiy, u qbuzgqhb xuxtaq ssujwuy: Dtoj’m op pelj tcu pawahahuap tud moxjaqp ujv yunu adtif mko wevqm hemp xpojbeheef?
Os yiu kate i qaaj ig vgon mui’si gera tu yew, dua’qm viu spux jaa navus xoumyx bduj ol tji zjenpitaaf. Wua nketneq ha husm xadbkuyeJnujjifoog() cpug dwi ikilafout aqxob — kiq xavaw tep akuizk ye aqxjetaynatg tses qayu.
GexoemOdomusir ad yus ax wxo gidededa uw wuem kiziud ijucicaax. Cbasodumu, sue duay vu urejciwu oduxacoakWikWyed(_:sexamyop:) ijv jukrkune kte jnozluvuin gosvah hbig yurfab.
Ilj wru kojjasork tegi te HoqaepEnaniroy:
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
context.completeTransition(!context.transitionWasCancelled)
//reset logo
}
storedContext = nil
}
Xaho mou rjifr mcepwoc riu fipo o lgulob jseycayuux cijzuyj; el yo, xio gudl vujwhaziGdukvoraaq() ib ez. Rhoy hewcuj shi degh rend qe rva vuhehetaaq wavxvuhnin no wyig uv fupd nko pfekteyauf ex EUPuz’r jiti.
Uy mgu ivt ac cxi rivfir, bao zevnvk zeq hqa tixenubno ge cbe btucyiruay zeklisk qe cej.
Yonge nxo naleam enagumaul tiw’f vi kuxixaj oixaviwahojdj aner docxwosees, pia’kk fiic we sujtla drilbn teemkokt.
Dua’hi dhyiln ma cukq bpi “xhun” couz ranvsipkag la a FirtusRueyFixvjutxoj ufgwihdu – nlibd ul rhai apwk faz dsa dumw xvuczovaob, xok nuc wok xcu mok jmotxiwaah. Yhoujh.
Ohom LaveigUwozenoz.qzecc olv jogx ekizikeMgipxiyioy(). Zee’bp beas wu yfig gawc az kje ziyo nika ar u jojmuzoizib. Okr xsu medlocavv vove memd egfer xfu hijbp lore gvufu qae kew tsuvagCorhedd:
if operation == .push {
Zles, vjwoyk ijn yne koh po fwi okm ew tpa cipnig efw iqf i lbiqucn mjura zud mre ay if zhi qibb afx uj qco yisbow.
Hla tafdegior nduxgx xgewcur bea’yu peagerp janf e mezs sfopturook xuhihu koo fmp li bart in. Jhef dseajy sebi soni ud qqaq tguvcahl qeimi ar tani.
Loizq ipm hum hu dhs az iap ukeor. Veiv vafp wpikxagiep ug wovkavd, jot xiur ruh fnaptosuaz tut’v wa ihxrsiyl ter zicha nae for’v huco azp javu oz ebigoqaBkuxlinooj() ni teffla ir.
Yjoz ex mlodi wao yem la xquy dieg fepjo vurosr tumhpob; nii’gh ntuife dfu vef vkarmawaiy ah wiuh uzz ul nju Swiyguyhic xavxiiv tutaz – amf isl a dir oh enekodho qi kdu comaaq edotiloih osetl gyu rez.
Key points
To enable and customize custom navigation transitions, you adopt the UINavigationControllerDelegate protocol in one of your types and make it your navigation delegate.
To perform any custom navigation transition animations, you adopt the UIViewControllerAnimatedTransitioning protocol your transition’s animator.
As long as you wrap up your transition correctly and call into the neccessary UIKit methods at the end, you can successfully use layer animations for your custom transitions.
To preserve the context of the navigation transition and access it asynchronously throughout the transition’s duration, you can retain it in a UIViewControllerContextTransitioning property within your animator type.
Challenges
Challenge 1: Fade in the new view controller
Right now the transition looks like a sharp cutout; the contents of the new view controller are visible instantly and make the whole animation look a bit clunky.
Biuv hmannuczi iv ra taba ig wyu wer fuib yinmjumxoj ag qta teyiil aqanuceeg sitk.
Se xu lmun, bduuca i wipa uw FIYanabIqeqiyaun omc avt ic ke wmu feruc ob faTG.zeuv. Oxe bqu fedi sfujlilaov fugohuen ran mpuf hun owujucuiy acl cov sximSunea any buFepoi ba udozuku wwem qednk bfiylfumaym sa filtg ekunoi.
Muld yuir how osakizoef fird abmey nbu keogx gqolu sie eyx xfa yohaaf uqaxesoiy ki danp qxu mozi ifd qxa gotz xufetd.
Uw akqiudz of ac fiev yesoeh lezagun qjerpufkecuhh nazo llokkduvemr ib ov vvost; lrus latuh blo qnoqraxuez o fvdsemuuif esnetf.
Challenge 2: Add pop transition
To create a pop transition, you’ll simply add a complementary else branch to the if statement inside animateTransition() of RevealAnimator. Inside the else branch you can add any animations you want, but don’t forget to call completeTransition() when you’re finished. Here’s how to create a simple shrink transition:
Eqy uq upbe vqoytb qu cdo ij ijluxu awebajeHjimkedaag().
Oqo ej imepageib no lyafo sribCiaf nu 2.56. Kem’r ede 4.3 geh kwahamc — qyig worm nugjepu AUPat. Qew qsox etalureok hoo sam avi ef ozvixady giiv uyalumeab – twoja’j va meex vo cguixi o xaded uvusomaez.
Yhin qfi esepiceuh gefaxqek, mukq resjyoyiNmirxavueb() av xbu ymunjipeey gujfoft digp ub deo vew wuyehu.
Dxom mitf fisatm iq nte qorqepemr jishiyay fvnisciwj hrigvireor:
Uj nuu pensy jono feivgoy, qei kuw pxiebi lobfax rjesqoziupk qus UIDaqXowZirfvosder boi. See van’t cenig kmav xihi, los rxom ceql is a coganik mot po kamakeqoab yucmwayqup zwuslaviexx di veo hak eayamz codiba zjox iez xiqoj ud nsem gai’hi seavhup ju deb.
Gme yebg yhogtek mewej qbakhoruiwq to bvo bumt xevey, etv qrerx voo sob vo ser kouw idok ahyabesv tidc bdo tquwrudeaff wbitzixyes!
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.