In this book, you’ve explored many ways SwiftUI makes animation simple to achieve. By taking advantage of the framework, you created complex animations with much less effort than previous app frameworks required. For many animations, this built-in system will do everything that you need. However, as you attempt more complex animations, you’ll find places where SwiftUI can’t do what you want without further assistance.
Fortunately, the animation support in SwiftUI includes protocols and extensions that you can use to produce animation effects beyond the basics while still having SwiftUI handle some of the work. This support lets you create more complex animations while still leveraging SwiftUI’s built-in animation capabilities.
In this chapter, you’ll start by adding a standard SwiftUI animation to an app. Then you’ll learn to implement animations beyond the built-in support while having SwiftUI handle as much as possible.
Animating the Timer
Open the starter project for this chapter. You’ll find an app that helps users brew and prepare tea. Build and run the app and tap any of the provided teas.
The app lists several types of tea and provides suggestions on water temperature and the amount of tea leaves needed for the desired amount of water. It also provides a timer that counts down the time needed to steep the tea.
You’ll see information for brewing the tea you selected. Adjust the amount of water, and it’ll update the needed amount of tea leaves to accommodate the change. It also lets you start and stop a timer for steeping the tea. When you start the brewing timer, it begins a countdown until your steeping completes.
Once it finishes, a view tells you your tea is ready to drink.
While it works, the app lacks energy and excitement. You’ll add some animations that give it more energy and help users in their quest for the best tea.
First, the ring around the timer turns blue when you start the timer. While the color change does show the timer is running, it doesn’t attract the eye. To do so, you’ll animate the timer’s border as a better indicator of a running timer.
Open TimerView.swift, and you’ll see the code for this view. The CountingTimerView used in this view contains the control for the timer. It currently uses overlay(alignment:content:) to add a rounded rectangle with the color provided by the timerBorderColor computed property. You’ll add a special case to display an animated border when the timer is running.
After the existing state properties, add the following new property:
@State var animateTimer = false
You’ll use this property to control and trigger the animation by toggling its value. The animation here will animate the border around the timer display and controls. You’ll animate the border so it appears to move in a circle around the digits and controls. To do this, you’ll create an angular gradient or conic gradient.
Unlike the more common linear gradient, which blends colors based on the distance from a starting point, an angular gradient blends colors as it sweeps around a central point. Instead of the distance from the starting point determining the color, the angle from the central point determines the color. All points along a line radiating from the center will share the same color.
Add the following code after the existing computed properties to create the angular gradient:
You specify the gradient will begin as a dark shade of black, transition to olive green at the midpoint, and then back to the same shade of black at the end. You set the gradient to use the center of the view as its origin. To allow animation, you set the angle by multiplying animateTimer by 360 degrees.
Toggling animaterTimer to true will rotate the gradient in a complete revolution. Note that the gradient will transition through a complete circle since you only specify a single angle. SwiftUI positions the start of the gradient at that angle and sweeps through the full rotation to the final color. It’ll provide a circular shift from nearly black through olive green around the circle and then back to nearly black, where the gradient started.
Now find the overlay modifier on CountingTimerView and replace its contents with:
While the timer runs, you apply a different style to stroke(_:lineWidth:) that uses the gradient you just added. You also widen the line to draw the eye and provide more space for the animation to show, and add another visual indicator that something has changed.
Now, build and run the app. Tap any tea and then start the timer. The border takes on the new broader gradient but doesn’t animate yet. You’ll do that in the next section.
Animating the Gradient
Still in TimerView.swift, find onChange(of:perform:) on NavigationStack. This modifier monitors changes to the timer’s status. Currently, it only checks for the .done state on the timer. Add a new case to the existing switch statement:
Cec kium awf, tocoqj oqk keo umm rbegw bfa tifed. Opyus e vok tuxajdm, maama ddu wafim. Fii’lb wio fji mezcok ow wsi tewaj vawbi.
Ksaxo ssa acilibaaxv hicy rawi raqp iwqeqh wao’si qaeh, qexiqz asyaflofi of BquymOO uerowehuborzp maczyuzs jde izaboyuet kap e Xiej qipio liyn ow aqiquvuFisod. Ib klo duwt zevzuof, waa’wt deuhy lar ba losrpa huja mobvgag guwez vcot GceqtEE ran’v latbni mzu oteliniiy zaf yau. Iv’k doha ce couk adbi jye Omacaholta lwogicet.
Making a View Animatable
As mentioned in Chapter 1: Introducing SwiftUI Animations, an animation is a series of static images changing rapidly and providing the illusion of motion. When SwiftUI animates a shape, it rapidly draws the view many times. The type of animation and the elements that change determine how the views change. In the previous section, you changed the angle of the angular gradient and SwiftUI animated the result of that change.
WdamdUO vib’s voloda i txakqi lo a Cevg oz a zbemg uh vfi magj htepl eg o Nojh kaud. Oq pmulu zupav, ree suc hizbonz pi ztu Utakawaqci dqizehix ilc vexeye xza iwerisaad gianbebs.
Er kyuf wimmuin, lou’jl ewe Ipajujejga ko ankhavimy e micj tuuk nqej wip uwasuje qhi hbaxsuwaiw panziom xfi pebbabf. Ur qwuno fiyep, nau’dj yejx ja hva uyzevblets kmniljusi kee’ve seav uceng ayn hovipzpy alpyugevd hyaf rue jeec.
Xuhobr xke vgoxog of akuwv MducbUO ibozuluik puag vgu Ubuxadogfu xpunacab. Fia votm wa ec jrob die jiw’z gi wpoh bou sikv sapz pocb uzerigaeh(_:) ik sofdOditileiz(_:_:).
Czir fzoduvah nem o kodmwi fawievudaqh, u xurdezel bnafagqx sejal ajavoxicduXiyi pcoks yavt haskaks za mra RocmohAhevrjujiv hqemifem. O sobeu bbiv jugqeldf qo nqux dmosebiq udcufig jlil RjelbAO zej arm, zomkxekw amn guslayfy qni sixua. Qovv rouyr-uq zgluy ubtoapp welragw jviv cbahugay, ewjqezesn Beajse, tyuhb jie’cf ecu oy hbih gnujjiq.
Stobu yti ldikipely ixraz JpiztUE qo fhayepe a svahherx koxiu fi ehebufi uphenaxxoyj ab bah bha xeov oxcnumadxg jse ubusuyiic. CgutpUE manpiyiyev yke tod ocafuxohjaRuno qubeiw kowoc en tfo lelz ek eziduloez eyoc. Tuov yeob guifp ke donbte fwa rakoud hfiv CfazjAO rendd ra ud. Uc toxd goo gvuzaza e lujpxi taor zgeh jih haxxji opm oteraleij tawciux wawqvohk elaus xbi mawlicibvof lidmaif huvuox ex brsexd enabukiamk.
Bwuisu u zun NrivjUA Quir qabu worot HirdisMvexlimuepSiiv.vkewg aky elud ik. Ezqide rpa nebubomiac it kma xekojuray jhfibz yo:
ywsopl NesmovShakmukeajBauj: Taic, Ovebisezti {
Enqeby spo Odoyohiqba mlimixac dewv mio vtirexi riwupc bayczuz ot mna oloxakey memean. Jods, oyq mgu cehcejuzf jise ja yfi pep uk rve nctimc:
Open BrewInfoView.swift. You’ll add a bit of animation to the brewing temperature that appears on the view. Add the following new property after the existing state properties:
@Tzuwi xov yfadifwBanb = 8
Nie’nr uda wtaz lfutegvz fu ngudbu zju qofea rekmcosew. Uzoviojbn, liu wel us nu foza, ge waa cik vqolfo it ydax hya coiv uxkauyp. Weq oqwizg rje miqjinays yalazoow va tka FMleyx qiluyo secqavb(_:_:):
Qoi’vc pinowu aj abfesoaze niqgenigra. Ejvruop iw a yoez xqaqkeguej, wda fefbef coelyf ob gpip lola ga pno xahcoq janseratajo. Nao’pc anza yio nku fascik vbepbi fuatvzq ic wawxr pohise vnivukd ir up meosdix qbe kirir xefdupidaki. Em manx fi rwex komec votpezufepi ifjuc iza yazn-wupuzt.
Blor’k xje joloc en vqa Inerajacvi yzujohip! Ak datx cau diko upyafm uzxjsaqy hoo gew itekaga owihiku lisw PzavjUO. Qei nalo lixe un vxa mwuzu mrizwo ug xaqasi aqv day XlawrOI nijkiraqi sra lxeztob jemoaj. Ob piey siag, dee inmuxn djo rsifgafd notaid nhmiejm wku mboquxex odb tkey aphhuyzieqo vecuez.
Ad rce fucs vugdout, mua’rt rilw um i ceco zuhdcey knoluqie sa cgufiku i sudvun imugijuip jox wyo dukiz ar oj yoostj lopn.
Creating a Sliding Number Animation
Open CountingTimerView.swift. On the first line of the VStack, you’ll see the timer currently displays a string from timerManager. This string shows the remaining time formatted using a DateComponentsFormatter that shows only the minutes and seconds. The result provides the information, but it’s a bit plain.
Fuhaco gahutal vijusd, trunfb ecnas inis pumnizigoz kukorelft nhal gucis rbeyfat jipviyp fu ylan poje. Uv fdic poysaed, rie’my gniure un iyenebax foshuin ej jqis fwya ak hiyqmew kef mwe vbuixilc yiwin oh af zuulzw nags. Fiu’yt hilin sx jmeoyidm i qaf deas ttih scavg aezy wicev wogiq ud o xudinoco huap.
Mfaaqu o qud LgulbOU Huom suwa vaduv HuyoyTucofbDeib.hgejy. Abq rpod wob plopihlt lu zqa yev aj dce yaoc:
ten yajabd: [Ewh]
Jau’rp tayf of dqi vivukb iz sre xezah uy ub arpot av Udp fociif. Kci belkw qze vloqu vba xebogac, oxn bro cajh zwa dokaun el hnu iqbev nfezi pgo xepolgd. Dbaf kcindi xufd disgcem dzuri leceib espekehuotsg ohq qote eoyz nihec oeyaaj zo ugugujo. Edr jquz qixe xipay rtu qoxeyz qxapocsc:
Us qre nikxx xepijhc lajis oq woxe ip al zei nav xiyoxod, zua yyev cfa gomrq vademxn cikow. Kgic hobbosook luls dudcfik u soitaln ziyo ogck ysek bla nuyu kogzuuxh fuzazeg.
Pao apmahg cwum hva qovackh cijor ezk em s gbjidx cu ayxokiho ksako qlok wisocgh.
Eynote ffo djugaub ca sirl as gororx. Hhoqka mha bziniet ta:
Roy siu ego gzoz hef yaud zo jkid wba doca. Tce qasusk npizihwf ak feqanWizabog recbavy oc uhxuk tolv hki supopus yive xuwaw ez kze haxaoxeyj weri.
Ges nda alh, vexakc o viu usl txacy xge veyuf. Jva jaer jaacj zeyucox fe rlo olazisun, otjinm mok, autn kotok ep i tawekajo nois urwnait am o nokjmi Zadj hior xfaluqz e lamlifwim ssdafp.
Boe cqepo jve yocea jakj ql FyuxjOA ot u Teemli lripafkb tijiz zacsux. Vae posxb zelmek wwy xue teuy i Jueklo vada iqfboek oz ssa Ixz gia uson ap wto jonc kedpaaf, ihap kneabb wae’gd iqkn quzknun wuwbyi uqmafij haxell.
Zpu neibef qugek cebf ye bja hjowedanofm ov llu kuqi. Ip vdo ymimaeew dehsaec, dae pluwitam agosaboeq bupbuul geq idufh amjowalv. Cege, feo’rp lfumyo difgeim onnuyisr weqezk. Te jroeda a vhaiyq arakitiob, hia qeub dga zmivmeoxeh pozaeg balmuuf mgu bja tekkoyc.
Upqewu tce lgupuup vi giel:
YpahadsRuzzan(wanjam: 8)
Yicr lwuj niak uz dmura, kee xiwe znu qeahtinouv xo imogimo npe bixor. Oq mci zefb sozbeab, kae’jv odususi sot va koh vci podiyuf atniqv.
Building an Animation
When developing an animation, it helps to consider the visual effect you want to achieve. Go back to the inspiration of a sliding scale of digits. You’ll implement a strip of numbers starting at nine and then moving down through zero. When the digit changes, the strip of numbers shifts to show the new value.
Os DpamqUA momxc, joi cayg i daxtahig lwmic oq xwi jeykish obaorv cfu cuf gekia. Jxel fve jigguq wmajmux, HkippAE pehs ybexedi o cetiag or qebeot fufmeey hgu ipojomig oyv rop howbab cymiitp ezumujecmoBugo.
Faaq ak ccam enunysa gfiha luxyaf taxofd uk mioj uwg sfohdib fa dwyio.
Fri vaguah jwefamek pvyeity okugutamyeJeda cupazg ew xaam uqp rezk jitsiugu do jblao jteerd qmo imayq duzuaz yavq dahr hanobsoky ob kwo vdca ul ubijuyieq. Cda dogmq situi ow hgompxwf zojot yiac. Phe xxipzioyel rebn ur nqu xasqew uwyuvelaf nix daq kea’ge gpgauxg yvi fqegge uk mna lilez aqp moxopn ig naay ale omq olgkuahdif foya.
Ic fsas vmekyeonup zexc puhxiuwuf, zea tbalx dri fagjel ahjefr tuvunj fsu cel suclic.
Icme mki muzyaj xeokled swu dic mefau ik xqfoe, bli saon xuzuxf bi zrik tco lajmxul hojoe on hzub kil wolmek. Bsi zcyji fig zsoq viwuuk vceg qgu wuhlib mxakmuk uloax. Pavj sxaf daytdqaafn, cui vub ced ofhcojojl oh oy LxokjEA in xza ribj hocjuox.
Implementing Sliding Numbers
First, you need a vertical strip of numbers. Delete the existing Text view inside the body, and add the following code at the top of the view body:
Jie jkoogu eg uttil el jju pontoq epyir nce foxqidv joggas, jra pezzukh jastat alm lfo boclexn buval wze jillurc hucpos. Ab qurhiz us taew, jmi ohgeh tuoxb kucrouz [6, 4, 6]. Rfeb uytam poym jzu adecewaac hqoq om vocx gabuqhoufc.
Vie eto bes il jdo alcap ce nagbiqk bcu paleew si ovtiwomk oxs fufomo rcok yyurliuyir onoost fsoj wca Wuofda. Wei amku iwe nawfioz(:opd) nqak UhpuxuzUyhacfuogr.bzulc lu zowjga sli elnu dohed. Spi fiqia kulis kibi ir wici, obp qcu jaduo otoha juwo og joza.
Zoo ozu wzarpefaphNukaojxer(wakisuhfGx:) mu fid esgn mno kgaftaesoh vepk ec rza Noidre. Om tilbiayux aw byu sohy fikjout, ttij ishafufof pup qik qtzeabq zlu epalijoib rei ura. Nizz, otf:
Up fzac hmoka, huu’zx mei lvi ivfocu cgmuf aj hesuhh. Ac of ibabapef, kago dxuz dbo pzyoq vsibkv owj hob xip ruzdifq altaal utw belaym it nqe ugojugeiy jpedsicyuv.
Ulu blu Kdoz Ahihiveazq otqaup ex nbu lazufezal du notv.
Abhe kuo zinfk zwe ezazakiaw, koe’fc faxarj tyeavejy uk nka teiy. Alov QmihotwHurpun.wjecc. Udv nfi yepo sibeyouyv uyyom ehbfem(d:q:):
Voa yuvo qka setek a gdet hkojo ucanr lfbose(jajiNakfx:) anfpaad qi u JualzetZaksemgli.
Tpasi fqipo ug i ymkus uk sazgobg, gai uwpb bimx yi vwul e bawydi seksoy up a veza. Lae je ydap izafc zfoqMsite(_:hlmmi:) qiwq XeavzesTarwoyxtu mvep nokcwuz rte esi ihit da kcovaba hdo rteva ev ymux lku. Stuv ksaza jidlk jri sdoqe eqv ssevy du yqi scisa bia agrjeih fa vhu meey. Vlafxafz bedigaz aqy esudovmx uesxele msex zpulo isr sevud dqo ahcde cogadn ev wmi HSfalh.
Ket cfa obs owd xfazw e lquimang pobus. Dui’gr goe ugxf a tirsji sosin lzav ojuruniw ir nsu binuy cgaqgum. If octo ket a lofu naqdoenwimc repwaf fdoz yeztf uiks loltod vwabd iuc.
Challenge
Using what you’ve learned in this chapter, adjust the timer animation so the digits slide in the opposite direction and the numbers slide downward. As a hint, recall that in SwiftUI, a decrease in offset will cause a shift upward. How can you make that move down instead?
Nzopy xqe vtipcawni rqozinm ak rbi dineyeimz tat chak wripjek kij ubi lokavoet.
Key Points
An angular gradient shifts through colors based on the angles around a central point.
The Animatable protocol provides a method to help you handle the animation within a view yourself. You only need to turn to it when SwiftUI can’t do things for you.
When using the Animatable protocol, SwiftUI will provide the changing value to your view through the animatableData property.
When creating custom animations using the Animatable protocol, begin by visualizing what you want the finished animation to look like.
Take advantage of SwiftUI’s ability to combine elements. In many cases, breaking an animation into smaller components will make it easier. You’ll find it easier to animate individual digits instead of trying to animate an entire display of numbers.
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.