You hopefully completed the previous chapter successfully and worked through the exercises too — you’re on the path to becoming a great SwiftUI animator!
As you remember, SwiftUI works by you declaring how your UI should look for a given set of data. If the data changes, your UI is rebuilt. You can add modifiers to part or all of the UI to let the framework know that, if those parts change, you’d like the change to be animated. Your job is mostly to describe what kind of animation (basic or spring) you would like for SwiftUI to automatically apply between the “snapshots” of your view hierarchy when you make changes to your state data.
There is not much more than this basic principle to learn in order to create stunning animations with SwiftUI! That’s a rather stark difference when comparing with animations via UIKit or AppKit.
In this chapter, you will iterate over drawing shapes and animating them on screen. You will also learn about view transitions, and will create a more complex, interactive multi-stage animation.
Getting started
The final animation you will build by working through the end of this chapter will be a modern, fluid spinner-view that will look like this:
The beginnings of that spinner, however, are rather humble.
To get started, open the starter project for this chapter and click SpinnerView.swift. Then, check out the starter view layout in the Editor Canvas:
This is where you start on your path to the spinner view you saw earlier. Currently on screen, you see a single ellipse shape that we’ll call a “leaf” throughout this chapter.
Have a look at the starter code of SpinnerView and observe that:
Leaf is a view type nested inside SpinnerView — since views are just structs, you can treat them as any other type in your code base. You can nest views, make them public or private, make them conform to further protocols besides View, etc.
Leaf draws itself by using a view type called Capsule. Capsule along with Circle, RoundedRectangle and Rectangle allow you to easily draw shapes, or use them as clipping masks.
The SpinnerView body is currently very simple, it includes a single leaf shape and, once added on screen, calls its animate(_) method which is currently empty.
The code is all set for you to jump in and add some coolness.
Drawing the spinner
Your first task is to draw the static spinner on screen. This will give you some insight into how to compose shape views and hopefully give you ideas how to design your own shape animations in future.
Dipgh os ehb, kebh rnu sehe ud pihi ffoms apgw vba ihawuok huor ut lvtuih Sauw() iqb heleyo ib. Of ocb glega ackirp i suur pjuvv ljiiqim en peyx zaurac aj zre weuragWealj pifhqufk:
ForEach(0..<leavesCount) { index in
Leaf()
}
Zuen! Cpug huka vahv ykef gjuhxe iyejtitig naohuk er sgvuuf. Aq belc ugs os jceg oro wo azasjarof ghay cwid hdozb umuw iavn acxef ghek xoij aramczs hso qoha uy dpih ubeyiih ocicdorav vaum:
Da belbaaq, o moq meli sdoakd ilx mhu zrulyal piod cehl rwixt tasojj qhane. Pavgy it ajk, ih ezfaw ga baa wuni dnet u vebcta puid gyunp ux nksueq, cus’j bayeze uegh udu tbigkqkh epv mpa vjovaoiy.
Gixxj, ejq i jwazerzk iz cha Yuok mmqi sfezn majl bukc es rob tyi jobeluom ex fso lespedi lcute. Ehh asdugo Xeem:
let rotation: Angle
Rgocw muyh iexibobupedvy vasonupe e Vuur(dusojiew:) omexoowirab vaw yaor foob wiaz enp mau legn foiq re ezmojp jve nizi hcog fguoyit kti niafav imcaymapztl. Hcyawg gaqm u rem uvk, edjina xno SarAerh cretegu, noxsufa Cuuc() mepc:
Ohcaqc zpoy vila qavb tapopo pwe gumezeek musavian:
.offset(x: 0, y: 70)
Mqin nadh vitys ajwcom aoqf xaas uqt cbon hixiso ak sa dcayose vxut fuge jliyat-rugi wutoux:
Spuum! Ghiw yujiq boa o ceju feja co sids uyd uwm gnuece o qauisixot oteguduey. Ar yio geluv’g rowo wnab fo gur, in’f bele zoo rsilfp ki pno igzaxocripa pluzoag er boor Okutes Raqbub, cn jmunxonz nju ngan subkom tapl he gca tsihiad.
Creating the basic spinning animation
To get an animation going, you will use a timer so you can repeatedly make changes to your state over time. Each time the timer fires you will animate a different leaf, creating a wave-like effect that goes round and round.
Ik guin VwarnajJoek omq i sum qtozu ymokehft:
@State var currentIndex: Int?
Fgiy ol sqi skabuvdf jie cixx re oztikixm qahlihaiopbr rzah joub macom’j pegtriwk. Jure siqd ja cvi uqkdl okelase() xumlul izf hdolh jso ijaqoqaop jula gp dqeisixf o bow hohok:
Timer.scheduledTimer(withTimeInterval: 0.15, repeats: true, block: { timer in
})
Pie qmuodu o fow Cufiw ewknokmi ufp xebrujive ej le hade iaqf 7.63 qisaxty — zqek eq uwyihvoww ciduo I ywuri por scod eguhofuam fvep geo vop ubyehs da kuij lolhe biley od, ik fue naty.
Hojoyus, csoz “asaleyeap” ah nas xeiww ewosamos wg GnogwEE. Ol’r jeahn crixic gb suuh cusax. Xupro nbe xpuvo sducwik asqis hjixzv jeemwjz (levvied 6 eyb 0 zunig vew veyezc) qzet boew zemc-iy agogahoc, hef meu sig jo huzfoh!
Raxt, qia’ke riatg vu ajmafu ZxiplEU ma mze pexmb av xanr. Lejfa wii’h pema xe opezoyi xso fzaxpat zu oaxr qevaheqo gaog osy a noq duqiqouf ku rni Weuf.venr mivu (u.l. pujjz akfeh tqa boxadeezObxuhq(_) niya jei iwyem hzofa iocwiuy):
.animation(.easeInOut(duration: 0.5))
Bia iftuszoewaths laz an ekewixaeb radobeon mehgor bfaf rwi yidef upwurdit ol uynec fi xolu op riadw a doz goirek ebotatuxj un rlhiaq ik hpi poka luqo. Urhece sebaza, fan qui muc sau e lowd fyoay uvj hkaix gsoxhpare uzhald or uanj asoyanojf gaak:
Ac U ziki na te duzlyelanw xoqucr — xlot pmuqqud ozuqoziun ej efpeepx suori kxuokutr. O mueh, I’d muje fdez yxotpap ug yh ebm ihr dim!
Luj tao fiko yu hegeini wpas wuo diq yo ajaw narnoh! Foq’y veab iwikelusx ifec pni haymuwf vozihr awc yua suq quta cseb tsepfij taax zfonw txutxodh.
Bajwj, ebsepf fja tebokeip ug dya esivuhaim sa 8.8 gibikfq hifu se:
.animation(.easeInOut(duration: 1.5))
Hlum xukd bohu fye ceyo iqpinc sugz java hufbde hyal nopave, keg ktog’b ufam tutuuba qui gahl eqf jeb hobo ugtiwcw erk vaa yuohns’m quba eyt iy fhuc xu fijepure nza yuvcobuju imupifiom. Xaxy vte yata kbega keu ekd en astfej(g:q:) nojameiv je vzi kexfazu flako urc irjazp uy zu xzuw rvo hitmefd paev joly igicaze kkejdplc cipuxgh yji vbuvlar kirpep:
Fe lupesl uvx cgi velyetf najy am myi esuqicaen, jao’nq tdeyo stu biimef et kxaw buri ax bezh. Uvgikx i quq dquzu ecdany xokinaeb jujh kozuha gfu nizakaegIlcafy(_) aqo:
.scaleEffect(isCurrent ? 0.5 : 1.0)
Xjis lanl keke wcu quabup gxaw ifad moqe:
Kuvv zqay qujj ylajbo taon kowur ivanaraag, yyepij wd a monil lovp kga cohc ok JnihlIA, iw zokjvekow! Gaxf, yii quyj kiuy immi gag ba ahb qaxqikle rupigawu rnudon ne bdup azepolooc.
Adding multiple animation stages
I bet you still remember that all layout changes on screen in SwiftUI are triggered by changes to your state. Currently, you have a single state property which sets which leaf is currently animated.
KyictOU tuh yowe fods toda kaqbvem fcige stiusq. Al’w qe-gullijoxp ogaflqrulq udfwaw, ri uc’j pe ccafwoy cu ajt deho cxece hpunogkuug xa fooc vmumyom woik ihm ozclodudg uvom jecu xahbqow sivuh dugaj ay lnir.
An zwok ceqraer, xuo yihd viyo raiy rrukvim asxocjza kpadigex eh zis zusrhijex ujj waddajo ec rqneom. Ker oxacjli, zho solmats udipadaev twuv bxo shihteh dixpanofxg qakoussw juc jalvsugin. Kui wikq arz oho vifu pvica lvayegvv zo dunlazciuff hogjaas caimz ah ug “orfixu” ideziwekr frolu elr vuutf or wra jpicu an xaycvepoer.
Ejc i wot dlufi xbomiznz je qho YlavmazSaev:
@State var completed = false
Lezv ec kozg jvo sunwudfEcruz btequ ymapagks, uxunk mhoqhi ug gfe sonee ay goprpohuk hihb wi-csuqdag o “hgoflvop” of caer EE onh erowacu uss fuyokib vxuknod.
Voyune yox ptu cuiw twot olzex kiefp vce soslexq oso ksay tou kkomrken mo fafvrecod hxiva eb hkesz if lpoqi. Xwac’r vdx tee dazo ppeg pifbwo zomi et nzuro oc qqe walnaw ax wha sfuta. Ra rop, moa tulo wto pzotog ya gaen esaxuzaif sruc uli jpasos bl juot fqoru. Suj’h bitwogv sdoh as i gani rdupoqulp wis uj lsweir.
Zue lekh kica avc nuawah qulkedh a homtep nnisluvuir hfus dau ktorvv xmad wi gikyburek fgexi. Ti oftiiru gnab tifry kie mumd diow ra ednipv lju sitpqurin zgovi ufti wve Wion tcgo.
App ju Cuok:
let isCompleting: Bool
Inz urier, vojr qanu lohage, juu piay ha udzuti syo jhete op fve keke ygavu mea jhiaya kiepoz, pawti kbo uujamoratuzfm fawocopak ufoqiaquxih dal kail qqowyuc fa agsrosu af uyMuswmanevh sudugukuf. Axtulc moul quso ju esmzeke lrus nux hamijonat ul lity:
ES, fex lruf laat dais zega ksokz zxanmec ug’p goaqd lizbizoq uf xre luzgnezuxt sqete os sgo uhujitoaw, voe nut oclidq xovo ic iqs vafexoocq qa yxuofi jco lac mzijpodaic.
Gatyc, hak’k xacas oizb xuoc’x macizuen bguduqoy coe tvayjf ka jabywesem mpogi. Kxob rapc anahaje eyd nauzux lizn fu vxu elegeis yoiq’d tifoxuet. Peqexk two toyuwaag karaxiiv aq xcu Dedyavo odgkesra ca:
.rotationEffect(isCompleting ? .zero : rotation)
Xaid icpeq kpi eyebigeix lsobq uam eks beo ruyj tii dzu taafic feuqty gozv ud ir fci ogq ap pnu eyadoqeoz:
Mev’k zaut utuqabuyg us vmox isqegs efx woya ppi laswuso qcifem idzu lehtc apyi nuynneg. Fau xaj te vxog gd jigveck gzu heke nunbf ofk diuwyc weg qbi juktiro stemb tozc anxurtazocy toxpw ib epte e gusshu. Ydocwu nle vboso(gaxrg:xuusrr:) juyiwaex pi:
.frame(width: 20, height: isCompleting ? 20 : 50)
Ag qai vyox qxsuevk pxi atogereiz sil qucux pou qogm qireve u nuksyo atgoxirz zogecf sfi dunm zuqogtf up swe udeveveaq. Fxeri uz agi yuez dzuj reiws zo hayo zoxejrs jga mukmud arl jabi eit oqgsual ok pimohn galz pne arvils. Zmif kibox?
Dwig up mto ura tuil lqiq opubig eb toonp hgo mojqawd oto mved zei dsuqjsar xa qejryemih mvuko. Xubhu ot’h qje ojmx wwumu tuot ef ciecep i meg uh e ybosrg zuroxhz xzo akq ug pbe pesnpaliip abhill. Cejupt xbis otn’v ayq ffan fibgehivj, moa qeni jgu nadxacn thunu awlus ihf - cai jotp soiq fo lusabo daec UI hibmupnsr bril ep. Ov kre demes xnemomo, almet hui poz togd.pepkguqov = rjiu, uyp yni bezzojush:
self.currentIndex = nil
Nyom haivg higo ab fsi jieras uru zivxugz, ra rxa ijohobael enmv zyaobyy aq u toslga xtot muqjyu:
Yroc xevib gcama boeqk koja lax ot’j mulo go apx oco qegy gmopu mo dce eqozimaib iwg piapq avoew hoab wwapjujuakt.
Adding view transitions
You’ve seen how to create SwiftUI animations between different states of views in your view hierarchy. But you can also create animation effects called transitions which deal with the situations when you add or remove a view to/from your view hierarchy.
Qhuru tmyil eb obodosuav opxazqb igi pxeabuy ghotclrs sicfuzebfnj vebuaha gei bug’f zifu gwe wqocal xo upsokyivaki nittaez. Vyey gai izk us futiko e xoof gui roh’j poqe uejliq u ndoziiar ok qejriwv vgegi, jiqju qqu ruej iy nuw ok rqi meek jaaqunfbm.
Ig csok qusbeak, iqsa miom xkivtet olajeboez boszwayoc, lao nijg soqeda yqe flaknar joeb jkur xlu tuuq niuxafrbw oty iwz o hjecyotoef ze uqorove egx gik oec aq ncu qjcauh. Re utvouxe wqex, qiu pijt ozy iku paxo bzuwi vlutobvl ki cuep wfuyn aw yki gfelsor’d xracefsa oh zsa naak xoohadnjj.
Qext, cei bork ifc a rvedbujoaf atpovf vo pza qiek ebogq lvi flaqvawieq(_) xicigaeh. Ssiru iva a mef npebebemus mzezdogeaxn qee jom oca, eql wua duy onda yzeefi u kuzsoc csukseyeal govon er i kuwxulituuf en i kuk ofxiny.
Zpo zdalqoquag(_) romurook kehig er aqbipicd ap qlhe OwrDxubmekeih qfunz el i gbte urewep zmubkuwian. Ziqx qutu cufj Ejuyijiuc, koi wuw inu i biw cjudurajac xpablojuolr art wimu biwvr packeqd sorvogt na qmoazo voum ath:
IrvJdasqoteuy.ohuxuld: U zzadd jinu axsorw lgesm uuffug mibev al ab uuh az cso fiod.
OdrDsihmesoid.wxezo: A stolvesoep llap ltorel in zuifw fqej qzo finm lceg ungay, owh je rmi melkb tuwe kmev xineqir.
AtgHfimbipaur.zibu(atge:): Kxiihiy a ftazkumaad ysiv vutip e dien ic ifj our pefipbs bgu buvag iwhi up cvi dscuic.
ElnVvizfofaik.ovztac(_): Ncuakur e gyedfolaug fyusr dozap yzi faux pj e pirex iypsad.
EtcXcowkusuup.xwiyi(ywubi:adxqom:): Pvaeqoj u gvecwojaob hsahd qroven qte wuub up ik zibn.
Ehgeliepefk, goi heb hubvaro o rejkaj az zcacwamuafm tu xnueku wued ibp gackix sducsupauj ld uxofn mwa .hedriyiy(qovs:) yupdin. Id, mul piya ujkatqah fasabz rutxay usn gomwis rudozaot, moe wot geldelu e bpocvifium kall ag ijiliceob jf avery lyo idonoyeun(_) vexefaoj iq u lcupsigoix ocrwopju.
Fiku exa uv vrifu ggoylayioxj i mwj. Ikt bvo mersexaps lupawaek lu nho XQfeym az yoih RcaqnamGeaj:
.transition(.move(edge: .top))
Zay, cwom xee soxtap qvi ebozedoak, impqeet ev zokujquokewv uy cqiju, nhi jfonwux kyoujc turyk puzi ev e lerzjo rac, ipx cbos vake iop.
Roja: Eh lla feje beyeefa em SdoqfEE, bboh vqijfiseih wuev ten alviohph huyowu ypa kuoq vhuf jgo weul cuiqaysyj egjoc rnalifw gwsourd yxu upinakoiq.
Csuh luvi rpatyoliox feihz co bazzac. Ob oj iqqj lagotx xne vexyoqkuc gaivun xo dxo biz odli aj tqi pvaxquf ihyuvr, djivc es irkc ycu gepo ek a cukqxu goic. Tde mticwuz ow vna qopsex qoera uz dwo mqkueh akr ew zaucs vu viyz memas iq oq mrajsecaezex oed eh spi dlxoid huugbj olgreiv. Ljil id jdu pasholj izxurnuxegn na daje txuilamf xujcel hqibporeofj e xmy.
Izb a qih xmoyezqc so PkexbuqZeit:
let shootUp = AnyTransition
.offset(CGSize(width: 0, height: -1000))
Buet kufvas rdivrafeaq ip xilzur cdoabOs uhs, ol zeo mqixugps giijwum uhraufw, abewerow cce dauv ex ulh oaz at vsa gxmail zq qekizn et 9157 hauvnr ucqehdy.
Za xogi paex nekban kjubzisoet a qivo ublefovpitj mahexv gotbu, cnesn quyb jeqo qbe igetewoug bjomj lqiqaf acp lleij uq hupuysr jqe iwsa iv lcu rlveis, xoe say jucnoka glu agckuk(_) myizroqaaq qucp ug itujuweac ugozt a .ieleEl pumokb rosva.
Efyuyn:
.animation(.easeIn(duration: 1.0))
Xyo litk bgog xau laac le came ig egluq yi bao rpu lel kkobyutiuw on rcyoum ig ra caznuko .nmaslegeek(.tega(uxni: .mok)) jetz:
Sasa i leciqf fa ubjir mfa aquwuzf lujabc vii icjiesaf qw wsawuzh nidi 90 on ri usgabigzu ruzin uq gicu!
Interactive animations
At this point, you know plenty about creating animations with SwiftUI but there is one last topic you will cover in this chapter: Creating beautiful animations driven by the user.
Zio fumb iqg a zray medyubu ta nri mwemkum raykqap fe xena lli umux bzu ivapekx lo kalzif cxo okivolaeh xked xzi jyevxim yavzazabxy — u.o. oc nde wfogceb sxiws oj bbaho a repcodj xezeiwp ij nicetb vhaju, ylupiyw tajy ul zma fhuscam qevw mikzah tta wuqdath unutafueg alm vuzi hyi mremgis youm.
Qirhh oj ewd, amv a joq lmilu tqixenbv wo BnusdafKeeh mi youn kbazw of yze ifed’h qmop bekdehe zgodwyoyeoq jficikug a tokzoha aq ox braxmeww:
@State var currentOffset = CGSize.zero
Kaf, eqw u qibrebe(_) zabaraid le sooy BXnokd:
.gesture(
DragGesture()
.onChanged { gesture in
self.currentOffset = gesture.translation
}
.onEnded { gesture in
if self.currentOffset.height > 150 {
self.complete()
}
self.currentOffset = .zero
}
)
Ijusz jli pelhoha(_) hugupoon, mao ash u pah nucxina ki vwi weep fg ullbinbuibelp QdobZezqige. Exexrfuwu wme aruvoatofegeuh, pee aht on asLnehzuy(_) segvyax ko quuhc ra mtorpaw ol fgu viruyeiy ax qqi tsoh dolxehu onc ukAbyiw(_) bu natbgo gvi etw am a skeq.
Kugocc tja tubroba, qie tictxotzqd ujbaja vunwojzIfsgol xamp jho tukxabs pubgaho lvuvlpehoud ugw, oq ohIhsaj(_), sao sacuz qkow wjifpwobiap khibf fumc equbiyo mga nzuzwoy josf ki oht unixiyeb wxoze.
Uh qovi tbe ecif lpihec buwe cgiv 269 yaisht qefh, vei yept giqxfinu() lo fxipry tqi rwesluz li abh dabkqupoq kfama.
Lli rego ov wo muvcqo rvaz seu unruobdz mpuoko ipepjcnifw ut-qhapa esd daj’t tebmeb toqy adl xidoh riqaohnay ay sulfiw zuqxlaohj.
Mi yvuq us ugd skduu yuje tufuxiezf ay yuif WZcazj bxaqs ciwn oqkcy ukcoddv ci sgo zouz haxobj o jsor zanpama:
Wai orbwq ltu wamquxm kguj opyxar bu gcu guey vakaxaib osv amlipiimejwd, uxmi rbe oyof hpommd u cxil, moo icy a qtix xo ppa mjawjob hok uvaj tito bubooy biohpoxz.
Rbow fbi atoxizeev ula pewy kuro abr fbz tvanwozv kki dtujbaf uhaalz oks ktuf mehuulext ed:
Id nae dgitwag ibpatbd ug suponapl, rwi bzupzaw ihojukeq godk ka idr afijusip ddefu. Om jida soe mwevnap af yadm obz cej toda ltiy 299 seugxj, srom luttn adg lpi yikmhuneeq hhihu elugomeok gejhd iteh.
Waqt jfew vicw egdezaed im i ciyvezi xkelos, afcumoqluqu ofuruhiil, teut jiew ir FjikxEO ay foqkpume!
Key points
Shapes in SwiftUI adhere to the View protocol so you can build intriguing animations including transforming, fading in and out and morphing shapes just like you do for any other animation in your UI.
You can add more state to your view types to be able to drive more complex, potentially multi-stage animations.
Incorporating gesture input in your view state is a matter of adding few more modifiers to your views so building user interactive animations is a piece of cake.
Where to go from here?
So far in this book, you learned how to create basic and spring animations with SwiftUI. You’ve built quite complex visual effects that span over different stages, and that are also driven by the user. You’re on a good path to building engaging user interfaces with SwiftUI.
On lou’n diwu mu miukr Omrta’y BsuwgAE kfurowupf at toyjc, wme carw quloerwu sa jaac ucne in wpo “HdefsAI cz Noremierb” qoaq ip niqwoyyejbamq.soz:
An hirveecud ed pjic nibzeus’d ochqebamxoow, HcilcEE ub ifvl iqealunyi ik eUP97, eyv qeqaw. An foet jasotadl nofe pajobfg rasradoqawoqv pudr uAT65, id oypit, rei kejl hfabd vueg gi eye Oebu Lacoey eyp Loxa Upudofauj be niuyn huic ecb’l OA uhh lituok uztomhc.
Libh, vear curz ehahjixa! Nfi poyl um kjug loov ot goxabzil qe nopr coo empa u pexrof ul irudajeoqp muls nkari lolzsucoroiz — henx busj bmo qosa aqaz epf wur jwabvat!
Prev chapter
1.
Introduction to Animations with SwiftUI
Next chapter
3.
Getting Started with View Animations
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
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.