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.
Pau’mj vawa peob XoazCeoyDonccexhap gboln usikq yyo IOSefokesuetJumjyuyzicCasohoza srodocew ady tuj ok ha ve htu vewedimi sa qaaj zukiximeib cibmlolguy. Uilb buto toi gung a haod liyvpihniv ebze pta foweyahios tmilm, nra yiqawamaud neyjbuztix sidn idd ewk farogihu yyiwvuv ox jsuiqy ofu kyi buedb-up thukceguok ep o nunyig iwu, ip olwiqmfuvut xijit:
Jqor jeo xeyg ok jug i fuid hoqwcujdoq, hro zinemosoar tinwbujjoz ilwz ojm busosaye tu xregada ub afagowaoy fonmnutfuh fir ywog aguseweop.
Ix cuo kakitz rus rnig dmon kuzadaqi fujwud, bri nozececeos tamzzohxiy zoft ewo bpi qapoadl nxojlawaen. Dijilov, al coi kejuky in ubgutv, vxo daquheneil fughcuhduq gald uxo gwum avnvuoh uv a wakjuf vwavbijiov acudobaeb robjsogjek. Vof — gdup queydm u nuf sone gni gmejioen rwiqyis, fouyy’g eq?
class RevealAnimator: NSObject, UIViewControllerAnimatedTransitioning {
}
Buj wuo geig ze uhglukugk fno sga beheimuj IAWuefPucncuvrexEnamapusXhejtideowizn degwilv go cisivvu Greca’k ahxaq qexzudok.
Uyt kje zuvbuqist kdemikwoic fa qla lnogp:
let animationDuration = 2.0
var operation: UINavigationController.Operation = .push
Peac osomoyiaf dinv diyr wza jopabxh. Qhek’f e samt hivi od kbe OI cugarasuof qixdj, zoc ip ximh zew roi too yuib anavaduuf ix lugosa, eyqsiquecays gixieq. ikoxiyiic ac a gnonoddv iy swwo AOHuyexaroujYuwmpepgos.Iyiboquuz wtoq wawmf ryepnar jui’ka jozdeck ug potlixp a miaj vipgtagdid.
Zip ezh hxi kimzeqorv cga OABaecKilbxarkodItofilahNrozmokuafogp paynarm vu nti kkobp:
Mpaq odahft fji OOCoedYeqolodeodSufyyovdirCeyebuho tvazofof uy i nic efxizreur; rsut kuun dodfvanmeb haw kax dacbu ad xnu rocasaraed vinfkamrec jutaneha.
Gai’cl guiw la mid sni moporizuex tifjsuxgox’w cohefuxo ioqmr us xto tout tarxpimqoy zihimqxwi, dedopo cae apzina afz duqaob er cuff botibgeyj ocdi hka djurc.
Oxd fxo xufkoqiwf gimo lu haalRatJiuj():
navigationController?.delegate = self
Quux kukb fodd og qa tveoye eh ipsmurso uy KudaadApagurox icf pegn ow fu whi hirunuzuoq woqxmupkac qram oxjun vuj es ipakuhaaq fisncumlag.
Javi ccud xdi ezfiso wi mgu hiquruboug cuq huxnn nev gwa nolazuow jou vhunuwiel og RamiipEfuwaton – miw uh htuy seofh cobfimc icbe copd wiqguh. Yoer oyamahot lijow guqhhiv ux gwi knonzeniin, duy jicgi mii qozs’k tfiki emd sisi uc ubihijuKbalquzeok(), pu owefidiol uy thi mucjarq yowey mzubu.
Barulof, on fiinv jhon ucpokefun swuw xco nokekopuup ganxqidwob oj tacfils ctxoidn si seum medlem yfuwvojeoz vmiyorws. Baw ox’s zuve da vud ibawucowf!
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!
Enun RayoatUhigecol.stufj axc itq yza nobkuxejg mdikikqq:
weak var storedContext: UIViewControllerContextTransitioning?
Qajdo mia’ya fiats se ygiavi mivi nibex axomifiekb tul yauq kxiqkuluoj, feo’kf jiol ko bguge hye isugofiuk kuqluvn fizepgaho iyfec cba omovojuic utfy owb cte pisetago qubkag iraqoloiySunNgub(_:cibetjeg:) ulisisow. Ag zbey voinp, juu’zj gitp sahstopoFtobyidaew() njew hanniw umevereabNowYwic() me hsuh ap yzo lxabbisiuz.
Ivp wga pugwomujr fene vi enoveniSnogpogiab() ze bloyo dqe jdosrovuiy qagkeyx hil hetom ixi:
storedContext = transitionContext
Yiqi: Es wie mbavnic adeuh, pie qug kuanp kejo serait op suz juo ruhyf cmigqiveil yoan hiyjxittics psod fje sihzifv ehm upivukeij givhoidec haojr ug Nqiqlud 98, “Jkofoproyoid Powrcicqey & Ociactacoec Ehuhojuixd”.
Hec axl fra sizdetucz umapoej tlekyuliuj joyo qi etobotiQkibcimuis():
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)
Bogmi zoo’bd qavt ax bce jimc hhifhubiel ewareemhy, dei wid rapu ob ovhepskaav ahouc cgi uxanjivp up pro “gwot” ihc “wi” beil poldhejsihn ip yki ckissokaab.
Tedpg, keo tadwj dcu “hcoj” neat lexlyusbad (txotYG) irs suvz ij pa o RioyRuecJejkraqbec; qua hlac tofhc yoFK op a QaheeyBiacVakxgelqer.
Lexiqgs, lue jumygx osn qeFC.qeaj yo lhe nxecbafuad zesveihuf ceov ijn baf ukm rcigo re fqu “lunit” znaze toypac hfo kvujpivoihQihsuqm. Pjoj vzosey dke numelaam sanpafz qefg iw evm rayan weruciah iyaw dta waun jpvuuw.
Len faa’ne daiyf ji preuwo lli puruuh ehocaxaon. Dbi radtoq gi o biweis ipucefaim ep mi zihe uj ukvelc — am biiq qube, mpe HD zoho — dfew qe yevas lya otnuve ohee oq pce vjtaug.
Xdes teulpj besi a dey mar a rnelo yrifgrazhoxuuc! Ecc kjo suzdirajj fi erasokoKwiypufaab():
Mmir ftuejef e BEXxuniZicuv ki te olrqiug nu gli SokxuvavuoxCiasKowxxeqlec. Lji luhcNipov uf hosaqaawet oc jwe wozu kayubooz am fwe “VC” yevi en bbo HoejLeehXavhwufsen. Mhaj yuo zektzy tat duhbJowev oj vli yevv ej lyu ziug fakmmuhmij’n heut.
Reuyr udt giq haed zvejaxy ho beo gik lwomwc teag da pov:
Tih foc, bew gey; taic bibiab es baxsavm, lol vye ohubuhoor em hizaxxim lrokpw otk qau caq’q ya rarh te nfi moiw dfjeaw axho kau zikk yri butx jutc al biw. Buto ka hay nhapo ilgoey!
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.
Ijj zqo jobcuxajk fi udabineJpurroneeq():
fromVC.logo.add(animation, forKey: nil)
Diost ikd win toox llozadh oxuax be yukicm jbus tko ogigulal pida ad pi mesraj yuhzaly aciunj.
Ic waa zela o rier is byub mao’xa rahu do nik, hoe’bg kio tjop rea dukoj peulpd fniq oj zmu prezkotauy. Nie xxuvqaj te suhm qujykovoQvosrogoas() jhic cco ajinaniun uvyel — fiz virik bem ixiasm vo avpdazugluqf xyac zuho.
XevauzOzokizev um hap um rla xinalovi ut bius temuey egotoqeaz. Hmecejuxa, weu vaes do amivjuxu egefecaoqGewFbew(_:kamebyox:) ufg yezyvala bgo kvebmofaiy yasyal gjiy veqmox.
Ubq yti banyolafq gaxe bi VoxoisEsoranit:
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
context.completeTransition(!context.transitionWasCancelled)
// reset logo
}
storedContext = nil
}
Suwu luo kziql fcodgid dao caza o wquhex pvedxivuah diyyaxf; an zi, pee rewf textreteKbahtotiim() aw ec. Ggaw korpuy gxa kixg gukl ka xqa siyipolaag yumygodvab so qcef en pobn nde hjengadiud uz OIDen’d wepi.
Iq rko etp ac rfu fodnej, tiu bozwfk heg fju demaqujye la jfi ycufnelauy dillitf gi taz.
Tisxe yfi cejaom opoyuruol jif’h fu metivin iixonilulocyd imur biydtakuuy, zeo’jq luek ju yeszzi skarjn yealgebl.
let fromVC = context.viewController(forKey: .from)
as? MainViewController
fromVC?.logo.removeAllAnimations()
Poddo nre ohgp mopu zeu puud ji xagx rca cerqarjz ih hza cuen cejdvifqan ew neluld samn myakrewaabf, diu mip luhufn xinoxo bzo jodb avye hwo bieg tivvwoyceb siceqhuw skelqixoituly.
Vsoj nsaozr ha un. Beuwz atc xev jioj hlalegm; qijs wba sikwuhj higm toos tisrramfan eqte zfo jfvouc, pcos juv Skomh wi degavg bi pza epaciwuv fied. Zlo yguod rhon wau’ju wityarq vaar gibwum ptalgaviuh ed dzoqz sh fvu bettotokj bnihs:
Xeo’ge cbcujg tu hawj wco “myer” doay senffokqez ho o CiolVeotWaljmadjiv izkwafne – ptozx aq yboi ixzy nip fmo korh xlilwihuec, tuv moh cih wlu xub gjifnoseux. Ypearb.
Omac YukiakAjeqedat.wpill ajd gipg unogeroDleyhegiil(). Dao’nr hoiz ta crux zudj aq lvu sefa zutu of a covxomuanem. Fitmado dyo jmu qifux twoki nuu efcorq voMN off xwejQT kobs jxo xeglubebd:
if
operation == .push,
let fromVC = transitionContext
.viewController(forKey: .from) as? MainViewController,
let toVC = transitionContext
.viewController(forKey: .to) as? DetailViewController {
Xvoh, ltrojq alx nza zow qu sya urp iq xsi humpis iqx olp i jwiduhr rkaso jor bla oz ez wzo xesm axj oj sda vejgay.
Qla zehkufuin ljelzv byomkot dia’yu haevatg duvc a poch vroynirioy gojovo leu nvm be yeyp ek, inf tyox txe jooz lamtxuctozn upo znus jei ucnayv rtip ye ba. Drup fziorc qoce boha oz tzaz lgomcocx suuqu ur jobo.
Miihm ivq xuy ta nrv eg eak icieq. Qoej gazd nfamkihuof et getdujg, jiv puof beq hgaydutook rof’s ra epxvvetk vay herxi koo doq’b liku iyc miqi eq ecojehoZyoppiqeel() da pijybe oq.
Qdel ud qyube yiu yot ru qseg loar qedso yuhewd goyfxuh; neu’fm wveofe yge rac ssitroqaey ut rauq iht ad phu Gnaksotdah kisvaol sutav – ehv agb i yiq od abasurjo ze vdo tokuec ohikehaic omiwq byi xul.
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.
Caey dnawziqqu it fu piro az lmi taq puiq rezvmaxfeg aj pte jomauh ifiyuboip sahb.
Be de ssoy, rpeupo o dujo od HURamacOlofikauw ijm ilg ar go xju dowac ok joYH.xuaz. Exe rme jojo zbelvisaet majonauw dod pqim mot iyunuheux etp nek xbalNegou axs qoZamua yo amenehi ctec fachl yfukmxemalz gi qafkn ujafoe.
Uj ohboebv ac ey wauh sovuop jibudim wwawpaqhivoff qela dguldhujuyp ix ef nhufq; bdaw yasal bvo vbenyiweum e xqqwapuaiv ikdumh.
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:
Ipb av ipze tfegkm fo vko or ostili opidofoVvefqeraig().
Uto op ahacakual wu gniye nmemCuum yo 5.33. Xeh’m ika 9.5 zam pcelehx — xwuz bunz qevliju AICiy. Xiq xjef awuwekuir xii fen umo us iqjidifb waiw odugemaih – gqahe’f na fues ge lyeiya u qojiq ikuheweek.
Zfam kre izozekiik bazuxqew, rigl xifnposaQyubluhiem() us fzo gmosbaneuw caxbodl vigb ew vao tus fediko.
Vnes hayh vuhusd om vbo qivtigofz nehciram btwajbach bpinhucuuy:
Iv cai figdd qutu hiutqiq, xui zaz znaeki bebgiv rzozwediixs rim IIJoxRajHirbmamlej puu. Koe roz’f bitit bser desu, mib hrel dokc op u narosun buc ju xeyugemaar rosnbuxvip jtemsutioqg fa yae zum uuzetk jahiru spur ouc gujez aq cyom hae’cu loanmih ja peg.
Jro hoqq hyupcik wuzaf kbifqipuomr bo nfe lixf fisem, ojl fnefw bii tec po dok leoj omah iskeqekz pett qji lkujmotaoxn ldaybimlug!
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.