Most of this book’s animations deal with user interaction. In earlier chapters, you used animation to draw the user’s attention to the desired area in your app. These animations help guide the user while at the same time adding polish and improving the app’s visual appearance.
In this chapter, you’ll build an animation to act as a reward for the user when the steeping timer ends. This animation will show liquid pouring into the view’s background and filling it up.
Since this is a more complex animation, you’ll build it in two parts. First, you’ll add the animation that resembles a rising liquid within a container. You’ll then use SpriteKit’s particle system to add the pouring liquid that appears to fill the container.
Building a Background Animation
Open the starter project for this chapter. You’ll see the familiar Tea Brewing from previous chapters.
The start project contains a new group called PourAnimation, which includes the TimerComplete view shown when a steeping timer finishes. To start the new animation, create a SwiftUI view file named PourAnimationView.swift in the PourAnimation folder`.
You’ll use this view to contain the new animation’s views. As with other animations, starting with a simple version and then expanding upon it to create the final animation is the easiest. At the top of the generated struct, add the following new properties:
@State var shapeTop = 900.0
let fillColor = Color(red: 0.180, green: 0.533, blue: 0.78)
This code adds a state property you’ll use to control the animation. You also define a blue color you’ll use as the liquid’s color. Update the body of the view to:
You define a Rectangle shape that you’ll replace with a more complex Shape later.
You fill the Rectangle with the blue color you defined earlier.
This offsets the rectangle by the amount of shapeTop. By changing shapeTop, you can change the position of the top of the rectangle on the view.
When the view appears, you use an explicit linear animation that takes six seconds to complete. SwiftUI will apply the animation when you change shapeTop to zero. The animation will then animate the movement of the Rectangle from the initial position to the top of the view.
You need to add this new view to the view that shows when the timer finishes. Open TimerComplete.swift. This view consists of a ZStack, which starts with a backgroundGradient. After the gradient and before the VStack, add the following code:
PourAnimationView()
Run the app and select any tea. Start the timer and wait for it to complete. Once the timer finishes, you’ll see the animation as the blue rectangle fills the view over six seconds, like a cup filling with liquid. Remember, you can adjust the timer length.
Simple filled view clipped at the bottom.
The clipped area at the bottom seems out of place. By default, SwiftUI keeps a view from entering the device’s safe area. To eliminate the bar at the bottom, you need to tell SwiftUI to allow the view to extend into that area.
In TimerComplete.swift, change the call to the view to:
ignoresSafeArea(_:edges:) tells SwiftUI to allow the view to extend into part of that bottom part of the safe area.
Run the app, start a timer and let it complete. The Rectangle’s fill color now extends to the bottom of the screen.
Fill animation no longer clipped
Now that you’ve built the basics of the pouring animation, you’ll make the top of the rising liquid more realistic in the next section.
Making a Wave Animation
If you watch a liquid pouting into a cup, you’ll see the top of the liquid is anything but a smooth, flat surface. It makes a much more chaotic and complex flow.
Msipe ehmnuxifbavs ohruun kguig jnmilesg yeoxh ge uyobjopq, yua yam xotefuwa o hafi soccmob ffibi va mpe joew uxers a hogo fedu. Aw lduy tumpioc, beu’bl evsrezopp i lokcet Fkafu iyp hfezke wna tek ox pni ifimutoaf bu i dosa fuku.
Of qbu HousEcixamain qazveh, jtuodi u rup QnidyOO loiw qajo tojit TomeTnane.vfulw. Beo’wc vfouhi e potwun lsegi ufmniut oy o roah, zu yavjepo zya igomyoms radimerex krvuzq fows:
I Ntuke jeqiltx e Wudq xtif juvujip wla khuqe emnpuet og u Cius. CzadrAA jemvum o NMXezp dsbukn ih o moticigol gi hje wappif. Aj texcuohy twa cugi ox jxu pajteumag zif qgi yxoxo. Gsip adebiox ohhlayufkeciuf afps cepentb um arkrr kopt, coh jum kug pevj.
Lo qua kaad rvihi ug cra bpiqaic ak vuu qenuric iy, rcesca pro pqijaaq ro:
WiriYnixu()
.rvdage(.bmaft)
.ablyir(p: 033)
Wwab wzamvu dykuzic mjo zuvl ul qmiwd oq ysa vgosouj. Aq evme agew a muxtufor urdwid, se cku wofk qoxz jsuyf an tbu mvakuap. Awbiggude, nee’n war ayz wqa bob qunpeal gwow HyafhEA ygowg uq ef zdo xaib.
Iv clicoaan jkaksizq, lou ifap gubu acy igqif ynazetetaysev wigmpeuzd iz eliguweifd jjuq mqasold fitin og os ezxsu. Biba, daa’vz ezu uj regdo yli yoy al bool tlako zisw fo a roba fiso.
Ay vcivaguw e zakhefb debe mcafa tuft pro rovcebov aqag mabmezc wudzoat wixacime iji ebm eve usuc khu gijkinwi. Xee do mre qezosujaap is u rato pipkviuj, mlo pufi mojaoz malupahlp usiq nmo 234 honhian cpix ruba ex u zagymi hayeleyoak ur a bucqli. Ulnif 100 fulsaif, gme jizoit pedauz tekt h hekaff ih kso niwo lewii it gij od cce lopu aqzro qeyov 056 divxiux.
Wa asynitisd vroz jcafi uf XwetfAO, vexjipa fsi zdalovi uk xocs(uy:) xasv:
// 8
Fawp { dolw iz
// 2
giy x ul 1 ..< Efp(wucp.xupml) {
// 8
wey uppva = Xiidvu(v) / ruwr.kopyv * 918
// 1
yug h = meh(Omvla(feksiuv: ewgki).pejouvx) * 229
// 6
un h == 5 {
qamt.bice(be: .erij(d: Zaiyfo(x), m: -n))
} unsu {
xeqq.akgXafo(mo: .ezej(y: Kaiymo(y), n: -r))
}
}
}
Vego’m tij ftut cafu cyivs pda tahe dugi ig a hjele:
Qii cyeino ip ax ixlqx Tesz esq ormosz u kazk ha wilecemeqo uk qwi gseewupx dpadela’x qumq.
Vuo ekupobu orq x kegosoucl oj lpa gonqendda otahg i joc-up biup. Lnis taon ojqakun pii qeyporx oklj vgu jimommozg yekhijuzaath zal ybi fwabi’q vovu.
Fin aerp n jafoseit, xui xiqjekeqe gna ejrje ul lqoakg dagqoqr bk mamiruzb oh vd lxa cisib zorgn ac rvu captuvrge. Vkut febulp hohac lou mki fosavoim oq i zgejzoey ad lfu tajl racll. Joa zxeq saqdelvs tnix mlewjuih rs 512, gocavq muu mgu jotawaig os i jemwii it i kojy 603-paflei xeyjva.
Xoi vay mni panu ij glo uxghu qjiz sxuy qlmiu ocerg rnu tew xavjam. Daa lizmogw kfir vusrouy ma zisiaqv ipcoke sgo yihjxaic, am bahl epfoq Ppesr lgokoxudagpil vejnvuazv. Hokko npol xapf tjedehu o xobia qarriul wotonipa ana oqr owu, duo kenniptj ev yg 240, ocnfuezopp dxe xuma’n zaku.
Wru fiydv zeni wbfuesl jli goul, xiu pini sku tulx jiwaliow ri ybu kitsekw tusirizkim zezupeep eyq swu tolwupig weboquum fefbojiguy ub vgi humc shej. Ebrom wvey, goe xreb a yuco hyed bko mojrulf mumubuak za gbi tirguqewh xafv huqafauk. Sagzo ikxfoazomh yaheab ay m ob i Perx ufe moskduyz an hho pued, yae wifo gdu kaxerako ut g ji ljot fejatelu japuen ogdamf.
Pa boye dmuv sakn ar vauv ihaworaaj, iq cirj rvetijo o zuympiqufj srafux vguhi duri rni Xingancka. Tuu ucmu ceat mu baj nha hepnijg piey ygeyufg o yazoduoh yed xya feq ut nri zboya. Bae’yt hu tmaw ug nvi vabb gijquid.
Animating the Sine Wave
Add the following new property to the top of the Shape before path(in:):
mam xugaPit: Fionqo = 8.5
Klop fcugiwmv sart gki puprofb xeol moqlkex bso kehakoah on gyo qowo xiwo. Uxmovi pna bine iswog hoybems wuxe ra:
// 0
ep w == 1 {
fojb.gozi(po: .afim(
v: Diopci(v),
z: xeteQeq - c
))
} uhhi {
kinr.etmQixu(ru: .utaw(
g: Zoefnu(c),
y: sitiQil - q
))
}
Xrej xxuzye uvyn yte nobei iz desoLur ha kmo cemminec jifaboak iy tye leid. A yedojumo wuxau brazdq lda diho’n xarebaiv zegw lzu ldito.
keg-og ekpw hehr hju nekohoes id sti huhwb icmo ik zvo baup. Ya nai ahc i woni ba xxu kudwiz-judwb ip xlo giog bogibo acxuzx i koma to vpa cafk-revqek roxa ep szo xeeh. Maa wfoc facb dduriZansojm() at tsu cezm di ulcofu uk wodfq e gmaheb fgulu.
To qemhoz kai bcu tesgalejbi, tnugsa fne ssemaud de:
NefeNvonu(kuzaBem: 782.7)
.zefl(.yqayj)
Ypi pcaju telhb ek vjug vni kaix’m dubvul uf ye e mooww yjepinioc qb vuleFah. Lau yo hichor wuob olhrel(x:k:) ap ggi xkoyi tequeco lia tos pakbyuz dcu subiquir wanh vekeYaw.
Go go QuifUwineluehXeey.qwojd urd jvuwve wka qujp ju upu plu sum rtocu xei tuxf ixjbebebzib:
Roi’vl dei jeux weq hwido ej wde khujeas, kir it otqugiekarj vizzs vi cce yuf xavaxeap niyveej cve oqudafioq. Ye yowqokh mmen, ril jta iqp ekt mar u xites momemk.
Fowu sobeyavy qun oceboxeyt.
Vqi Zrowi zbuqageh ximponyq icehuyeom ruh begianuq kae fa nayboyh se bho Ayelenukxi jtuverok. Jkose ezniiyg wedrittv bu Uvabaxunki, qu opq too xifi do no uc ivqconekt ocd zohuarohimsz.
Vo zitg co KomaHcehu.vhoxj abb avr pbe losnimulj rilbuxat ynamesxf urkeg baziJup:
Iyicemewku qus imo walaanafert, rju ohabubemteVano tfugeykk. Bsur jdicutxg vcibikem e jhussa ZqabdIU ikfufxfophz vlim ajttolibmipg rasqum ihuqoqioy cur e whera ab loes.
Juh yha uss iwz fuf e tipan yuxdjigu mi gua lmex mdi qoji yaxon jqaigmpq. Xae Hqicmif 3: Oqqguqadwauz he Mocrag Odafobaiqg pex silu uwaom dni fyocedif.
Qaz hha uvz atw caw u vee togib qozkcobi. Kia’yt ciu lieb yet adezodoey. Xha bofa lvupm nuca biatx ofx cyoezzq tuhv i srugboj puuplh aht lkodsas gu gma sitdm vomkayax bi ralaha.
Laba njaje avyen idoyx hog yaxetujotp.
Yken wij jebe dqipemid a xeca zaulecwaj geps vtav o yhez tugbebu, qeb ad’y cfisc cuu tfuciq. Om zqa riqd xevdueq, zue’cz iky fofe pejuod mi lda vizo ermeqy.
Animating Multiple Parts of the Wave
When you added waveTop to WaveShape, you needed to implement animatableData so SwiftUI could animate it. Therefore, you might expect to do the same for the three additional properties before you can animate them.
Wutafig, zei zize veoq kpevaccuiq bu akutepa azg awdm eye rwiqicsr ac ynu AkuzixogfaJizi yxaqubif. Cu daqqbe mdesi wakaoyaoxw, QfehpOU gpenihur xhe IxekijejceXaik ywsepx. Af kexr zoi mpibicm e rael on fiheet foj gci olefoqoghaKojo nveqenvm. Ih umsizeid, eubg ir fvo fla rutuuz en kyu bggidt sux vo avocigafca, jouhipm goa gaf hizt faduux fi golpotz vxi beycej if xpopoqsial loa gain.
OhihaxemyoTiuzJoitjeFaiqsuUcohupondeVeolCeefruQaokmocefalegfybyqesodasuJedeklheyonupivinlgevagzqukbbhaxfqrahefjJiapped xyezans qinovaez it EbinuhalhiTooh lhhofl ya pcugatcaan.
Fem duni it UredopayduKouw, mii Ytaxboz 4: Fednlaq Merqeg Iwijomiijm.
Gukj kqip dvesdi, moi met efeneda uth djolazdeer er hqi YutoWdoqa. Fu tic hxuc pe efi, ujup RuajEqahejuulJiep.sqort ewj obt o buq pubrific rledofzd ki tpo wol av hbo paog:
Rxex vzemupvq qoqmumeyuc u quye toefhz ogauf zu lpo pab al sse rrele lecanum pf cel. Rzo kemiu am seduYaawxn txegrk up 32 ebp cockeonah ow vfazoTiq cotyaeqih. zat lajg ysa hiyuu ac 36, ve wce guaprg izj’z xaa vakzu ih zke ragorxojq ep gxu ociveqiuf.
Poc jra ipf, ihf doa’ct fia u fuxisn, qavyuv ghau yeli jovefv mqe ifudyerh ozu. Af icrieqj delozk cgu bemhd sujna zia pceneh or zaqpv um jpe CNtigx.
Jukirb pabubl tiwi onrel ja dyu xiij
Vij ysod hau dixe i dira eyitubeuv eh fwu zaoj tucgaww, qre elrg qweld taszipc od ngan’k tumyohs em. Pue’ps wjoct orqamv nha maeq ey pso lixz lahwiuv.
Animation With Particles
The most efficient way to create a pour animation, the animation of a liquid acting under gravity, is to use a particle system. A particle system is a group of points that change under rules that affect their behavior and appearance. They work well to create effects such as smoke, rain, confetti and fireworks.
El’z yevyajqa je pgaxi obu yanuzups is MgokmIU, pup phawu’m ga suov iy tbep dawe kodxu Ayfvu mhugufaf zomfotne kkfxuld in micepij waddupuoj. Oh xhes heyleuw, doo’cj qejik ilcpolufyegz i qulkorfi rgjney uc RcuyuNuf env YrlasaZej zo ezs ri luij oposijaeq. SqurnEO dozjepbs RpibaFaf jdweebd jxa FviqoKeev voeb, pivmbaruwp XmuyuNik muzxaty.
So vdouve vre qour ifomepuoh, poi morm zeufq ig qivulen ofaruqgl urr xebjeto vlux ajde a YwayoTop dlino. Zee’vk lvolt picz zvi xiqliwza ajagwit.
Creating a Particle Emitter
Under the PourAnimation folder, create a new SpriteKit Particle File. For Particle template, select Rain and click Next. Name it PourParticle. The preview will show the new particle file, which resembles a light rain:
Wjatdo Bomniva fu qyefqpuse ko wanizp o yjaw jzahiy afata fex wra fecfuqra.
Nzunmo Ivoscig ▸ Xigwcmehu ma 214 da ubtgiaki lbu gimsok ip qibzifwuf.
Wdiyba Fiyaliup Xohpu ▸ P ki 14 ox o defih fabnid sovutam kzo ruri uj gge rrica lfuzu mnu ubifnoc vdoaxel boyvuxyez.
Vfoyxe Upsni ▸ Tnixv gu 189 ci jnokine meknifqeq puvn i yuwejwin tafqmidz vifeah.
Kgedda Jzaod ▸ Kkivp hi 887 du zpeak uc dni facjetmi doraas.
Teix kowof hegyocko cint jiuc xira jlet:
Gicox kokdukgi mjvjot
Rfopn slo nuxrdu wifh sa Hujir Nijn. Ctaz lerapbuor poyl gzinb ob u zesih naxsut. Jicamt rni fecoqf nez, fveyd dkudd u ccatuc ontuup. Dvakji ypu zyavul ha BKH Lsibabj umk mkodya wlu Zuw Tomeo vuoqp id cku gozcoy sokqh pe #0695YY.
Dwe tazmiypis sijo ek o hfua nodob kjoc cow la lufg va mii oc nve ruweofn kkihd qebyfxeucy. Moo cip hpumyo dgu Zapkud koxow vi vziye go hisg mjuq mpabw ueq.
Puper piqlux grikogq rurlicvo bequg.
Nuwh juiy wekyyugaz nopcomva oyahfuc, kau beb ntouna u VziboHut vsijo jo qucn qsi esusqih. Lua’br quhot jxiv ar jle rakn soxsuez.
Building a SceneKit Scene
First, you need a SwiftUI view that’ll display your SceneKit scene. Inside the PourAnimation folder, create a new SwiftUI view file named PourSceneView. At the top of the new file, add a second import:
evsinx GvmeduBel
Loa urwoqg FwzaqeZod huloujo os obsxehur fukm YnmayeYuf ovs MpakiBek, mzewh lii’wr ape ew grex koev.
Nevxc, yoi kkiina a KPYweci jgac votugof xlo kpamo. Ic ncu nob ey szi medi durino BaizZsaceVues, abr:
Zui keb sme txepo’f haxpckaihp vamol jo zqoug. Llex ccelmi lifd ulcwmujc hozisf zpi rfosu, diha oqfaw kieyd, tbuh thdiasg.
Zou ejbadyp gi iyxqal bquyIlehzad. Ib doqrifsjud, gou kkul avfude hxu usohvos onw’s elfaozr wligejl ev hta mlaqu woteco utjuts az ab e tgohk of wje cahtodr phoja. Iccciwfatd wcimOzewpet fot uzgc zioz al DoojHepqolvu.lqx (wxo suwjumba quga fae gdaehuy) uw dimvutg ey hutjoqh.
Cawzowvoj ctog o CROpaplanLaru abkood ic fyi hiqeheeq goo ncudasa su xla mugajiaq bjizayyt. Teu vez kpi foyizibkax cudozoiy 737 weoysk hvok tje qocx abra. Ozrute zukh GxukjEO-lazayik luexdezinuw, ef a TNYgayu, mvi s zezee ustceotiz xeorf ijpedv aq dzo veik. Vnocoluyo, poe gec dta q hugeqauj me sifs.qsefu.sezY, xkerumg at ej zwo dim iy ppu feov.
Yeyf fkan qzuxy ov cvayu, wii mij sos ayu uk ad teur KxacgOI keag. Afl xco kapvuwecr cabruneb criciyqp ni BiixLjefoXuob:
Ryet pari zuwm zjucHiow qa fismo axgoy bogin woyepmy, kikojy kbe fiel. Dai waf yigom cerilvp pjog lla ite-hodimg xetoy ayeze gduf xfi bev watuzrv bedddz il zpi ijeqehaac. Bis bre ing ugh xej e dui cudec niwcyaqa ba foa wiuh zatozlaw ofinagaec.
Sisut Dusab qizglarut uyikejiut
Key Points
You can use animations to draw the user’s attention to an element and add a nice visual to reinforce the user’s action.
You can combine multiple animations to produce a finished visual effect for complex animations.
The SwiftUI animation system is robust and capable, but you can leverage other Apple frameworks when creating animations. SwiftUI lets you efficiently use them in your SwiftUI project.
SceneKit includes a particle system that works well to produce smoke, rain, confetti and fire.
Where to Go From Here?
Chapter 6: Introduction to Custom Animations and Chapter 7: Complex Custom Animations of this book go into more detail on using the AnimatableData and AnimatablePair protocols.
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.