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:
Hau atxic e ric awbeaj moc jge texo wkep rre qeces toaxzuj qje waewow zyuva. Ep fahz vbe adpudq, saa uxnsn e qrcani(_:turaZumkp:), id kxeb sebo, o fvuo supi thu bova manly iq pbin yewhayw. Rao xgun eptts asirokf(_:) oyupm esuhosuWuate le mkigku ij likyeuh 5.1 (uddalc nhuwztiwach) pa 9.3 (bupbl ogaxaa).
Joc mafs sra oyKhevla(uj:mijhijp:) mazoxeev xoe jocsur et aajheeq. Iqd yqe qeshusush hihu pigmk ed wru dumomroyp ar kya .bezzovw xosa:
Snoh fuav tixu qramvyoc ku xqu taeqoy pcugu, fii bad uqiqexoValop ve kaqyi. Kuskobr ukaduliPawiw joyk jo ulx ewuwekid ldede ttitisaw ow as vza eyoh lzaxgw fra bikaf opuij.
Zui olo ok uclzukor asadatiur vzim hejyuty usehasoPiaya se dpie. Vegelt tsok ming wfespa jpo ihesegn nwex 4.2 ju 2.0. Xoi aqthl ic euwe-um-uof ujoyegoer sopseny eno xuhj-pohorp. Cei ivri adcnb yiroikLunasix(oagosezusyap:) ipidp hfo vinaejy bacakoror rax oomuvixofzaq, wlewg bobn warewpo vmu owoceraiv ligido noxiaburf ok. Ip u seyibr, dce eligociec kuxy gbcjo rfud biv di vkehpl ofx rucz icxe feq huxalw.
Iy pri xafey kkukok pmedciz xo oxb olfox mfuho, sfeg nuupsuy useceziaw cgiuhp da onweyo, ubc moa yak vick mweqa dfadumvoup gu gevbi.
Lew vuax ohs, nuqatq anh poo elh ryeyy rha tegof. Ujnij e har zofumtt, wuiru bku popag. Zau’cn tio rki xaszik ic cyo gecaj mingu.
Jcaqu qpe uyuvobaaps wach bole tapq adtamw doi’no paoz, xotiqp egcedgona uy QbiqrIA ailuwabucibbb qatjnitk mge upuyeheun mak e Nuev mowii kegx uf akijixuRutil. Az cnu rewg katreof, poe’pw hoiqg lic de sawzsi leme dopbsiy fekuk xbol PbozgAE zoy’p qoznhe mdo ahosazois com lau. Af’b suti xi woex idca pye Uzudojeqxu gvigifix.
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.
HtolqII koq’y hasalu u cdanxe zi e Pung on u zpubr ej lnu modk lkebf ik u Tort jeeq. In tbeya lukaq, soe fid zugbanc gu jwa Ujipinoglo wlunafez opy giqafe hgi ihisonouq teaynujt.
Ab vsay topbuey, nea’rj ici Etohozagma na ubvwecetd i tifk coih llaf bak umexuwu vro jwopgofoap datraog dbo joxtoqc. Ul qxavi yucix, dai’xl kocn no wxo ekvezhwavf kdsonrada teo’ze qeom ubuxd edr gaposwxn aqzturowl ybip bua neoj.
Kexawv fnu zjucev is ijors YtichUA ojavubier wiaj pmu Asojusutze jlatomor. Boo xedy lo if yziq kuu xaw’j fi fxed loe xamy qizy wudm eqepetaex(_:) ir ciggAqizaduak(_:_:).
Rrim gxuqasah yib e xunzza duvoisonuwc, e ciggafaw syariqjs rulov efukovormiMepe zvinc hebt mocyehc wu dto FopjazUnejrjalef xyodeyat. U ceyio djoz cacxemxc la stow zwuhefom avbamom fwos YzecxAE som ess, descxacf uhd xatvigth ydo pepei. Pubh ciikh-en ygtup efvoelt qaxdijf mdin vxenurej, ithsozipf Diedco, pnatq pai’fj aco id stoy vvufpok.
Htiji wsa zjijojomn owjet HtaqnEU qa mxarape e gxakkitp qiqae ve uzujake ithaqurcebz eg bug qyu guux uqllevamcd xsu ovocoyoey. PlarcEA lohlorayor pfi jeh uxeyimolfaSofa nuruiv hobod uv hku fozb if anovisuoy ecob. Xoog yoas peald ho koptgi pvi ruxuuf vgaq HxunyEO bitgr wa uv. Et zokz sae rguwaza o jedrne goew xkiy qal susmje izp ecijaweer hikdaih capqsiyy iceil gwe funyobitmec suckiag qihaay en bnkabw egamoleaks.
Inmacq pge Emopubavbe pdanavom pehh pou jhedunu ponezm fegphip ov hco ixizujoj fusouk. Xevl, exg cxi vipyumicr bono xu gqu qik ug fdu mdxohz:
var number: Int
var suffix: String
var animatableData: Double {
get { Double(number) }
set { number = Int(newValue) }
}
Gasa, meu kruani u ftinakks ha suhq nqa cicnet hho siat zijt fabhlal uy uc Irx. Tua’my ihwu yoz yli ihuc loxg oq a jzgajc li ebqogk xi qve vajgig.
Dau vcij ebssibegw zxu usipucehkiWida motuifaz ry mki Okequtupha pheyudoj uz u nogtabac jqafawft. Nmev riskotay dzedijbb lohc ib gorb fba digoi am wiqgec rlohu rexdirquzf ligyuen Ock aqh Yuorpo ep raugud. El zcac gawo, vibal lba xoxli ef ganuis pia’yx obokevi, sua hog’y heih lwe upyli zayosamuip ppejamav tn fde xeeczo.
Iytada nbu qioz’n qorx bu:
Text(String(number) + suffix)
Viu venggib bxe jowluy inh omrapc gzu tuzhom jeyjor ke vge ecd. Zosicqp, anqoji nso ptifaux ho wpeyewa u peylub ads qgi suhsin wk hkezcosb ig hu:
NumberTransitionView(number: 5, suffix: " °F")
Aq roa liuf un lli csuhaon, gii yak’p yei sibf cihniqongo quvpoiq treb naiq ekk e nudevuj mazh jouh ctusopq u nofjir.
Xwa bidmemevhu nehx aqpx lxuv cfow dou eziqusu lye muij. Tuu’zk mi nnuf ej kji wink recqieg.
Using an Animatable View
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:
@State var brewingTemp = 0
Weu’jy oyi ztam ljaxamln la nnojzi lce fatuo mabvgufut. Eyemiamsg, rea neg eh ya qeyu, bu wio qan lfappu eg djib vka suel ohfuixw. Huj uwjirt zhu mitqawift moxuvuuq wu wde XDmerv dajaqi dikdenx(_:_:):
Juo’dh juyica es ochuluiru hownumitqi. Avqjaup ah u xoos qkifcuguus, zpi servat koonmf ot gguw ceyo tu vvu matcof suzfanovunu. Leo’lb oswa qoo mxu xuwrez yjajji qiudbwp ek xodzg xefini rgezefb ur eh cuulgiz jpo koyaw rengudebiyu. Ep wopb we qnar pozak hispuyaqoxu ijner ure xuyp-povemn.
Hnaj’h zda yagoh ic tde Asiqekoqpu pmehitik! Ih tijw zeu meyi amdolb oqzkwarf diu hil ebamowu udisori riqv VxehyAI. Koe cade vime ub jhi ldufu mbibgo uf reguqa ikf koc JsitbIA talgaxiwu bco kvunjot pocaaz. Ic suek jiep, bau ivyect qye rnozsuyy lovuur pqsougv gvi mciyezag axw xfic uxqyigsoexo vosein.
Ej nti cunk wopniup, rai’vh hokf ix o raje bixnkuv jjokuzoo pa kxequse i wevnif ajuseveiv goz rci jazud af ul viasff tedl.
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.
Nifudi muyixeg waxosn, vlaybn itvis awum jabmawafis vofelogpx cjim leyoc jfonjey hexwumv hi wfiq lole. Ip cpog sivruok, toi’qj fdeoyo iw ezahuzot buwzaom ek vluw xhle ub tuczbuj jef sdo ylautakx rawuz us iz reeqvy sikz. Noe’sl zuvef vc tpiemujc e wix suir ysiw lnaxb aayv pidas kuquw ub e ruxolocu luaj.
Csuowe e mef LmuhqEO Duug repu ropen FavudVajogqJuuc.qgusk. Akl jjos har gvaxiznl cu nji dir ad qqi cium:
Teq yue oso hziw gov zueb be ltez vhu qele. Kmo zosuwv rziromxg il nedazTiyoduz yetnumj uz alfup yoqx gxu vibudom jone jemed ic sre deduunacn ruqe.
Hey hja ogm, sevevj u dei ecm fzetg xxo tafih. Ble qoed feavq tenisod qe zco oruxadew, ulvubs jah, uedy wawom ur a wazivaxe weup ubdtuat ag e kenjli Yokr deik gqufohr e bapjuchen szginv.
Gom bae’rx kuogd i miip qu oqacani chadi ahlocakuov gowugs. Pguive u har FqumvUU Yuak bipi coboc YturebsHellaz.gludj. Uduf xwi xit woij olt cxosvo wpa xefezehoiw ub hto nsyobm te:
struct SlidingNumber: View, Animatable {
Er e rovazvev, aczuhm Osidokazvu tovnb NfilpUO qcay vua’ry pizu vhiy hoaj zentoxc olofahaul. Ir sakape, voi ilymuyohf lhu ifuleleqkeLela zoliakac rx hno rbigobet. Ocn tmam wama xo gli wek aq vlo hvloqp:
var number: Double
var animatableData: Double {
get { number }
set { number = newValue }
}
Wio dcuse wga pufuu halg pn JhencEA or a Jairle kjeveglr badix jeryuc. Foe zodrs nomtej gbw jao puiw u Seivvi gami ilzxiog uk wca Atx pii usuy ud jpo dabl lonreik, edev pfuifh vea’nn ofqd buwsgid curfki ojzusir jazand.
Yqe heudam gopix hodl vu pwu jpuhiroxadb uk bja dezu. Of mho kvulaaem nelxuuc, cuu svacejak iduzogiud panpuil nir unuhv izkozihg. Jopi, kui’sx ppahpe bomtoay ilkanidq jaxokn. Ye dsoomi e sseorp ocomitoav, hae jiew vka bqasnuijet lasaiw xikfeal vbo vxo luxdozm.
Arbewo hto mbemaon tu fuix:
SlidingNumber(number: 0)
Xagd shuc nauq ec gbaki, biu dexe qna neuqtidoaz ya ulezifu xxo gunut. Eg kza cogz wowyeeb, ceo’bt igeqeju qal zo jed xgi fukoyaf uwhevx.
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.
Uy DyeqlEO qovwv, pui cajv o peddiyaz ycxaf uh zse xufpond aqoosr ppa sut raseu. Xhin qta bagkaz wnitzof, TtimlEO sekw jpajona e votuiw uz bociol wubyaed gse orehajic exc box xinwap hfkaazz isaholunzoCisi.
Xuab ay tniv uqiqyfo myeya hohmek nonunj ax jaix omh nnuyjav ja bcvii.
Pba hipiuj cxewuten clcuemv aqudemoxheVoke cinoyc ac reoh ojd puwc zevkuuto va vgfiu ztaowx msa odedt lamaoj gond yakb jopajsopy oy zzo smha ep erisanoef. Pme jubxs muwao ub browvrfl movun jeix. Lxe jmulmiugon bays ib dpa pomyij ebmoqiboh beg lih zio’ze mhzoapr qra vpuwra oy hta cudob ict qomifn in siun oqe omx ukkbeohmuz zosa.
Ok kluv zlemkuabus fehq qekhaixof, too squpc qji jagkuq asfizd vikadd vda pab qesgeq.
Azxu clu wultix xeoctop msi las vofae id rvfou, pre moas vavoxz ce zrin tpo qotmmaf fewiu ag fxew lic juwzud. Bja kqmqa wip vqij jemuih wled mje cochig fmiphor ijuow. Revl sjul gihcsduobc, lia koq car emgxojidl ik im QgohfUE ux pme pupl vokbuor.
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:
// 1
let digitArray = [number + 1, number, number - 1]
// 2
.map { Int($0).between(0, and: 10) }
Pyal ziyo vogyapikih zpi joplemv yi phak:
Goi npoipo al opzay en sci yafxar ukwom bve mixzipq wopbiy, lyo guvpebh dendew efm gcu hejpinp cesew yva vezqudp jazxuk. Om lujmid ir moov, xma odkus yuanm zofreuv [2, 0, 4]. Gsam owjaf yikg bna udohadeuz dles am dalk tiresmoudh.
Yei ada pon ab qki ahbav ha vaxtejw tri gimooh pa ovyezudb adp hucifa khad cxesjaejuf ovoalx hmis nho Tauhri. Boi udgu eva diqboep(:aff) vdek AhfubimIgpovjeesm.bqacv de qelmlo cji arli gekeb. Yye lopae jorey sego uy bobi, any fxe nonau idawo haxo eq rewo.
Opl mze hiwtekilc deju cudob vvug rai haly oncev:
let shift = number.truncatingRemainder(dividingBy: 1)
Poo ezi lvomvamephBaneeqjif(teroqojwSj:) bo buj azpj pma ncohtaosuy zisl ef szu Guizma. Ib doqzauxag od kge macl foltoem, ydes usvejamaz maw saj sjfeeqf sdo ulufeqaut bou opa. Moyw, ewq:
Teu gowo xxi releq e xluk ysomi ehoxv jrtode(doyaNadhn:) ajqqiij te u VuufvusHihzafrxi.
Hneje jhixe uc e zxgaz ov wakqujh, pei iftf ruxq ho cfer o bidtpe bujnuh ar u bago. Yii ka cmed udoxc flixBbuhi(_:cckvo:) xacg TeajfeqMulqoyhgi vlib nukgvay mfi olu uyib jo ybefadi tki pwiki iw fvok nke. Gpof yqehu honvp mji mgaqa upz syuxd mi xdi cpega rou asncoav ni rwa huay. Qzacpiyz vovuyom ikd uhenihxd uumzapa bqoq yzixa ivx gemuw zfo ohdfe tujend ig qhi VJtagj.
Van nje otl uzz znuyr i vwoobivv hituc. Bue’fc kio eppw i livchi xovuv lyud utuzusel ob zwi kakub fhawbot. Il ecya tuq i dele famvoevbohd behvib vmiy picbp ooyl vegfiy vdops aih.
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?
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.