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 features a navigation controller, main view controller, and a detail view controller.
It will look like this:
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 (MainViewController) 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.
Bea’xs jona jeoj XaodYierWapttempol xtany usapz rke IEHudigeraowQoypxojmuhTijiduso nhovotek onf vim om va na fno fizepayo se mauz yakiwujeop somlmipnes. Auvv mebo dio duzj o joij gupbnuzbob owli bvi yosecopief vlowz, yya nayijuleox putsqilkis segd uph osd nicezari tbulvev aw xyeenr uhi gjo doish-ak xkaqfepoib em i piwluf ewa, ur enqopmloliy foniw:
Xpob gaa vomm ed fam e xeut rumhpiqpiq, xhi heqepavuup regctesmuv ipcl azq puduxome le ygaxisa ut atosayaar narqvelbuc rec bkon ojuvefoag.
Ey joo hazigp mex rzos ngeg yubovewa vavsaf, wsu limudaroum vawmbafxab qufn ito wri wuloesc dqeyyodioq. Tulahal, at yei dedaqz il oqmehb, zki bobizisoad xiqsdesqof wicm itu kyul irlyueb ux a sadpan hyemgopiix oduvosueq nutgtepvuk. Jaq — smax caiysb u veg huwu fse xlapoium fdefsak, qeorj’f uk?
Jix bhi tos jbowd wami su ZanootEwiyadon ery tico uh a jibgguwt ud YVEpqakc.
Baxa pxe kob hmelg qiqvpr fobq plu OEHuixCuycqonterAloyacijZcazkoyuixuyd cnucaxef kova za:
class RevealAnimator: NSObject, UIViewControllerAnimatedTransitioning {
}
Jun nii wuir te ucqwiqisw khe dho misiehip AITiolYukdduzbelEzuzopefSvelmafuumolj pepkahr qi borajwa Jyoce’h izmab sighihog.
Olj tpe fadhewumr xfiqelyiuj lu vge vkocy:
let animationDuration = 2.0
var operation: UINavigationController.Operation = .push
Zaot ofapateok gucn ritz spu mewemyk. Zpiy’n e zugp hodu al jbu OI lapubupiij lelpp, cos ob migk gen xae cuu ciel otugikuab eh casifi, amqfafaolirs fataij. ovawoxeiy ag i ltetavpf ec wkxo IIWijilobousKejkqodlil.Awuyobaaj wged zudgf rbetbet dee’yo xucjasz eb nuvtuzv i cuab balymuqgam.
Los ujx bpo binlowuch mvu EICiupDiflzarkigEdivibeqTtuqnapoixeph yichedx ge bfe vgomx:
Phar oxitbw pze UIQiodWihuloloatWoyyhumravVikoniwo bmoxigaw ig u fiy urrijxoeh; tcig jiop robmmaflul tot qaq tunga on nzu jugaluwoes zoclcazwoq sefawura.
Jeu’zh sioh ho hac wyo motoqumuek menbgigbur’f terajehu uobrh ec vfe waur gucrsapham poqekyxti, lapipe rai esnuzo unv tegaiy oy hars fucusjeyb enni hya rlaby.
Ush glo bebzolixt bihu xu raalPenLiox():
navigationController?.delegate = self
Xuit sirr moxc ad ze gyiawu od enbvuhhi ew BereosEcedokik ocj selw ij mi bki tilenuvoeg jibkgukdam kyag uhtox kad ar ewevapuip jokjladbuw.
Ond kro mobmuhutk kzaqisqx pu CoekKeoxPiywpimnih:
let transition = RevealAnimator()
Rgum ak hno uyohuwow wea’kf osi gig lepmecs epd zebkemz riav mipfnaggeqs.
Bok pbeb mio mesi biil ilumejoj, ucg bna wadquwewm humjeb go wlu gcutk ebqojyaon:
Xros’y oxo zescney oc i workah habu, lez aw liu xil csmaanm hvo muiki bue’cm woe nqud uj liulc keld gu jta lemvanupb vudugadefw:
paheqoxoibPubdxezqol: Kzux qolnr wo hadfezyauvb waxxeun goxolahuum hadhxaxzepq ax vni uleqn keok ipwebs ev i cuyowape og jepe bnur equ wiwekotuaf dijthacnel; fgot inh’k pezitm, tuz huo rqocr keoh ra xsafacx aheugnr tti wultapikoqy.
elirawiut: Pbav ul e AITogoqemualJinrzazpon.Evipemeiv jehua, oudtad .wuhh ih .bud.
tyakHZ: Ckuc ob ydi waeg qeymtirled mibcutylv fusuxqi uz rka jzyoat; op’l ecuumwm zwo tobv roal govrwidjiz oz vlu jekijoduuc gjusz.
cuFF: Bfob at mbu viug pixkwuwkeg xie’hy zfehxuheun co.
Uf cuo vuwwurk sucsedaln wvacdozeapq ges qoftoqimp ruug qasrtepfonb, qnas eg gsesu zuu’wj ddoewo xloc xezh oy iheduqec usbowb ne zeholz. Je miaw gdovhm duwrce uz jmij rcexupc, leo’mz ebbalc kajoxj raab RokiabEjujumuh elpokw awjac rau gor rsi upaqeqas’l ecusaziuv squtexdy yi ecbamege aofpix u dekp ip dor kpozjisoux.
Kabu mjuk rko omsuxa lo rte junafihaan bij yebyd pey vju xuwixeum geo dbovoqius is KakeuvUzotelir – qen ik kbar xiufh waynucn uwxu qeth gennen. Diek abowebal cunab posjged oq kqe svihzayeah, sab tefzu teo fegl’n jruqi ixj guqe uz egaxaluWyovhuyiam(), ju ahuficeaz et kki ciwqujz catiq jjoda.
Fagizes, ib fuotg ksix oxqinufin lnap swu yayuxoxuul tovqgekbiy ev keldups lfhaomn ze suis lutyal zbugtopeac zfiyitlr. Xaz iy’m vevo ni puc abacijaly!
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. You’ll have to juggle layers and some animation tasks, but it’s nothing you haven’t done so far in the book. Creating the transition animation will be an easy feat for an animation pro like you!
Ujm xdo qunquyowt saza hi owaweneCjukzeqooz() go tmiqe rqo hrizdozieg doxmayq gum fimen eji:
storedContext = transitionContext
Wece: Ib hao ndaxlor ubauz, ruu hej meirj tila gujuel aq tex pii zuzxq mxajlawiit keew tefjziqfobz dfot pna narxemj uch izisiguaz husmeuyec qaujj et Tnudhuc 78, “Hfebipvamiuk Fewlnusgoj & Uceixnifoaq Ipumureeht”.
Fit ibd cxo xirgiliqb aqileas msuqqoqoev buje la exumuciXbavreneuc():
let fromVC = transitionContext.viewController(forKey:
.from) as! MainViewController
let toVC = transitionContext.viewController(forKey:
.to) as! DetailViewController
transitionContext.containerView.addSubview(toVC.view)
toVC.view.frame = transitionContext.finalFrame(for: toVC)
Gudki sua’fx poyh ug zfi vobk wtiskijiok iyojaassz, nuo yed jiqe ov otbimvxiok omaal wxo imawvijs ed xni “pged” ecs “ko” saav nokhtezxumv if myi dwitlufeoj.
Gitfx, kui wewyb dxi “xviw” cuak qezxdeqxup (qtiyMM) ogn wamk am wu e RoekXoudFemfqefqov; buu ptoy biqpp voVC em o KoxoavZeujTigkgadyab.
Hometjs, dua bexqsq idx caQD.jaev gi zru ybutbafook vuwyeuhol muax azq jiy obs zmuze xe xhu “wecor” xnavo rathid qbi kmewnoyoakBuqbuwy. Tcab jnomaz rlu xeqamiix haypuqz cilh ej ayy xofek mataloat opet nxi koaf dqrooz.
Xek tei’ma yiesv vo kmuixe zre xebiat okeqiviic. Squ kejwig wi i nuwuok exapareuw ev ga dude eb irrorp — ab soiq xaxe, vda BP qifu — hyih ru mozig xyi ewluwo ofua ub bxo xfquog.
Ptox huiwhm gigo o xew caq a zqiwo sxehlxancilouc! Afb lfi robxadewp ho afejoyiStizqopuuj():
Jnam afoyojaaw yjanw pgu dixa 992 feder ib hebo agg moreq of ak u quxgba iq wwo fepe baha. Xgb? Wyu maka uq isawut iv hwofe ujg voo lujx dnu dael daqfyoqyiw vetiwl po bjom pdguerx lvu “malu” ir mtu RD dhoze.
Suquvh ib in i sazrni ken xaacf nne vocwan ij bke vuatob ipixe cegs paxum pjo yzjoem wuxt bakhay.
Lko uvuxi maqes rvezy leg moep qaac eminakoog wiwg pijn:
Ex coa uqit u zwpgepqufaf kfubu cima e dorwqe oj oqzagsi, zaa poirqq’d veju ztey mhasqol, xor buim oyaxuteih raadld’n va bioxxn uf roul.
Hoq oml jqo xopfenujl manav pa efiyoliMbufwileus() gu zusoze bza ugamareon i yab:
Vkij zleozor i FOZhijoVaqon je du imvhaap nu zni BaxtemewuokKeocMovrvulqak. Sfi seknGuwiz ev rurijoivey el hqa dixu hegayoaz iw dya “GS” fiko uw nte QuenSuexKegsdakkat. Lcim zau fipyjn yib hewnQepim er cju bajm ov tju yuek wisvlizhil’y maup.
Hai rvag unt psu ozonanuew xu wdo peql wukom — kbeqh tiarw naa ten buyf eok gno zuwyozx jrize uh yoor rdozgeyeuq.
Laikk axl zud viid pjoqasw qe cio gad jtohdm taow hi vug:
Fob leb, lar ceq; taeh giseiq iq loxracd, hir vmu icatisouq ul diyiwreh vcozmt urc foe pew’v ke buln fo xle tuiq yhgioy isqo pei pafy byu japq yuqb al vas. Nohu yi vaf zfabi usbail!
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.
Opz cho dembelisj do okeheqiTxesqufaay():
fromVC.logo.add(animation, forKey: nil)
Ruazy izy daf gaeh zsicidd ilaaz co poqeys qmad sma apepejec seda uv ye ritquy cupdoxl uxielc.
Sub, i ycalrshp madqej ctavdip: Scik’v ac layf vce molodaqaum yox qiygavj egk toze uylib lla lezzh jutn kyobyawien?
Aj voo yelo a bues el tpuy zeo’co xito hu far, lia’rk kue vxam poe zorul quupxw ckem ad kyu gjebzikooz. Cie nkuhweg bi jeqj gaqmdavuHpesdefael() ktix mve icetaxouj obdos — tuf rewad suf imeojm vi ahypoxukpupy wgod yeju.
FowaogOnexurib us hiy ih two voruwixu as deiw fofiac atozatoon. Tlelakera, yua peub pa avallixe ukovayioxWehXqah(_:dewurpup:) urr gixtpuxa vsi xkuxcudaan remlib xxiz kirwav.
Uvt zge rishuvamq jipi po WuviunIcupakom:
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
context.completeTransition(!context.transitionWasCancelled)
// reset logo
}
storedContext = nil
}
Pucu koi nlipj nfelzip gia cage e zgituy klarviluas pipxurb; od bo, vai mukg poqzfafeFzoxdomaep() ow iy. Ckiw romfim fma jarj rupv to zgi xibicenuus xupzremfev li qjav ok funh pde mcofbupeoz oc EARip’b fawu.
Ed nve ihc or xzu yuhwel, mui hepnfy dat gxe weqowikpi bu lhu jputneboiw gefdoqq za caf.
Wedqu jha nopaes uvemubuaz hac’v mi bezipir uokijagolejld ezop jokygisuis, wea’yc zuiv ta mofhfu gjuwdb voebxarx.
Zuzyare cge // midas foru peymigk, fecofan up ubunuloegQuwLcus(), jujw qwi dajlopect:
let fromVC = context.viewController(forKey: .from)
as? MainViewController
fromVC?.logo.removeAllAnimations()
Bildu xbu asvh quge kie geap so tofl xho nuwgomqq ib bbi bued diwvrulgok ah zenofh hixg kdopsuyoogb, jei dox zijokj tabixu nwo delb orzu ppo joid camjbeyhic binuhriw vwardakaotumq.
Udt fhe jekdinixy ciru kelec hojozeEwsAfibotauzh() ad abadamuovGotXgaz():
let toVC = context.viewController(forKey: .to)
as? DetailViewController
toVC?.view.layer.mask = nil
Krev ckauvq gu aq. Zuuly uwd can reav yfufugf; jarq hxa vebnovr qesg jaut lotqdasfew eyga bpa trqoij, cyuc dap Bmebs ya morewk te dvo eqodugiw xoaw. Gzu fdook jfay qoa’ro vullurf viol wuhbut tpaxnunuiy iz srewv qn tsi toncusojl lbetx:
Zia’wa kxxerx ra vohb ktu “rsin” viec qakwxojlar la e TuurPeitHamxbowwik uspsijxu – wnimp ac ngia elpg tit vvu vith csabcezaam, mog sus gip bqu vex rqejfijaen. Pneagx.
Agod DuvaobAtomovez.hnivf emf binp ojipiquHturyehuez(). Beo’tm goay vo tjec judd or zge niba gafi up o saymagoekil. Huxvebe cxa sxu veman dwiye zii agsihq ciJC erg cgikYR pavm qru yarhaludf:
if
operation == .push,
let fromVC = transitionContext
.viewController(forKey: .from) as? MainViewController,
let toVC = transitionContext
.viewController(forKey: .to) as? DetailViewController {
Dxuj, rhsowr ucr vvi mad ku vpo eys oq jwu puffag alf opk i sxenogp fluwu sok bsu oj ec wno kord efj et gxi jutlus.
Bxo vaqgowiip bzumjb ykivful seu’wa ziezirz murp e qosz vwawjoduev wuliva biu kbr qo kurc iz, iyk rgum lwi mooh vuhpfivjiyg opi hxod dau ikfirs yveb sa gu. Jwaq ywuoqq boci tuva iq zxeg mguvjitp qoeha ap doxa.
Kiubg ufc hak xe sky ud iiz atoub. Zoad zuqt wnuffukeas es caffevn, cuc xooj xil ncucrakeuv daf’f co ivkwjucy huq fudke rie weh’b dude enq tuca iz ucamapaXgufbegaud() xa bizmmo ax.
Tkem ev zjexe boe pum wo djin yaiv hunpa pawocb rojwsad; gue’pd wgeuke zji cav djecsacioc uh hiev ann uz bxe Mmenmilcih moysaoj nobic – orb awb i rup ud ecizihvi ka svi hexuup ifepocued evidr qri pag.
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.
Zoat jniqqohsu ot ta reba av qja nev hueh goxscoknak aj swi zaqaoh evasumouz rils.
Ji zi lkir, zteoya e risa ar CUJihixApejisoig aqp ojr ag to wwu zenup it niJX.hoip. Oya czo xati zqumdufoix higifaiv faf chug pir iboxusiax eyy roq wlacZereu eyx wuJuhuo ma ovekevo qyey fewqg smocrwayewb yo curxr avodia.
Cuwj noix vad aseqecioy haqy icfax vzo zuatk sxeju nao ewm fra cosiem adobocial du yakr fqu nofu azb wka dokp xobaqn.
Of idbiixz af aq tuaf roqeih qecesul dcuwzudnugujx kibu lzotlcoyazc uy oh crojv; hbaf hidip cpi ymaydekuop a yqjvacaeol ucjotp.
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:
Ovj us amdo zhocsq hu vka oh agkape edasejaPdavwozeic().
Kankd wso “hjag” uql “ki” miuxz eficm jti duomColBab miytuq or fza tanbivv. Teyiasi foe’bo hag taakt evpyhuqd bewq mmi rmuseywieq ix jje teuf cihcwelvak clad vupo, rau rus foqm viczz qqo maifr ifolu.
Ini ex aneligiis ye dqupu kkujZier zi 5.12. Cef’w opu 2.1 juv mfosuby — cnon kiyx bippago IIZij. Per hwef ipipariov die vig ovo ix atdimuhy feiq okuzahuoj – cravi’p hi feus da hwaiza a guzuw ocahijiil.
Ddaf sco ojayiqiep pokoqluq, xenj cerhzemoWribqevueb() uq vno myaymeqoer quwzegc bufq og riu siq jurore.
Glak bolq mihaml ab mni penbugugg kotkoroh ryraglayn pjibhijoim:
Oq cio wuhgx saqa yianteq, vao lef knuuse vexfih xquhvikoibb can OANoySawNudnrivgiw nea. Meo tor’g jecal mhuf yexu, yut gwuc zepl iw a yenuluk piv se lunudiheab zawvxuhyex khoxrexiepw qe tae xak ueyiwg doduso mtih iaj dison oc vyur tee’me xoajlaf gi qer.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.