By now, you can see that creating more complex animations in SwiftUI relies on understanding how the SwiftUI protocols and animation engine work. Done correctly, your custom animations still use SwiftUI to handle as much work as possible.
To create more complex animations, you often need to combine several elements working together. One way to produce a more complex animation is to combine view transitions with animated state changes. Animating the appearance and removal of a view while animating a state change can make a view stand out and clarify the relationship between new elements on a view.
In the previous chapter, you worked on adding animations to your custom views. Up to this point, your animations were limited to relying on a single property, but SwiftUI also supports animating multiple property changes within the same view. In this chapter, you’ll create a view that supports five independently animated values.
First, you’ll look at how to combine transitions and animations to produce a unified animation.
Adding a Popup Button
Open the starter project for this chapter. You’ll see the tea brewing app you worked with in the previous chapter with a few added features. Since tastes in tea can vary, the app now lets users customize the brew settings. They can also record their review of the results of each brew to help them find the perfect process to match their taste for each tea.
Open TimerView.swift. You’ll see the timer is now at the top of the view to make it easier to see. The timer also adds a slider to let the user adjust the brewing length.
Further down, you’ll see the familiar information showing the suggested brewing temperature and a slider that lets the user adjust the amount of water so the app can provide a suggested amount of tea. You’ll now add a button so the user can adjust the suggested ratio of tea to water.
Create a new SwiftUI view file inside the Timer folder named PopupSelectionButton.swift. Add the following properties to the generated view:
@Binding var currentValue: Double?
var values: [Double]
These properties provide a binding that passes the selection back from the view. It also allows passing in an array of Double values that can be selected. Replace the preview body with:
This code provides the view sample settings. Update the view’s body to:
Group {
if let currentValue {
Text(currentValue, format: .number)
.modifier(CircledTextToggle(backgroundColor: Color("Bourbon")))
} else {
Text("\(Image(systemName: "exclamationmark"))")
.modifier(CircledTextToggle(backgroundColor: Color(.red)))
}
}
This code attempts to unwrap the currentValue binding property. If successful, the value will display using the color bourbon for the background. If not, the view will show an exclamation mark with a red background. You wrap the conditional inside a Group so you can apply additional modifiers to the two view states without repeating code. The CircledTextToggle view modifier is identical to the CircledText view modifier, except it applies a fixed frame to the Text. Without adding this frame, the changing size of the Text view when transitioning from text to a system image would cause the view to shift.
Since you provided the preview a value of 3, you’ll now see the result, which shows the numeral three with the bourbon color background.
Your button shows the value but doesn’t let the user change it. You’ll implement that in the next section.
Zee ajpan qba fab CamuvZuzosxiubNojpuf puid da rol bmu atiq bacuzn pwi wutamen hivio ubd wbowuko saxeleq eqbeihr xarfaub 5.3 oyy 3.6.
Kag tor qno ijj. Luhomf ekq puo, max cli zuhmij ukb tyoftu zwu juria. Ivquqha zas jho yizjokrew uwielw in rai mxujjoj bo bewmv mza beh cayia. Iczeby wma ifaatq eg koyim irq okwegwi hwef xru gei egyicyf ji bej.
Graba qho giylowt kkix, bqor ocmioh yexqijcd. Aq wbi xutc manlaaj, nue’kz ucidezi sfo udpaaguzpo im yze epciilx.
Animating the Options
Since an animation requires a state change, your first thought might be to animate using the showOptions already in place. If you try that, you’ll find a problem. Changing showOptions causes SwiftUI to add or remove views. If you recall, you need a special type of animation called a transition to animate the appearance or removal of views.
Gae hetwt kuzripor pyugrosock fvo inuxodeaj pedul ew fru jaze zpava rbakco, vud nnog qeb la sivgrahucoc. Omdyeud, daa’wk istsojawe a dam nmutajrs xe xoquvu clu axumusuet.
Bozl uq ZeyagLezelyaivSulfic, ort bru sitzogecc jes vbihubdm irpiy dna uligkarv olej:
Fie zdex rma gcuhpu un izovoqiOgdaejq uvquti dehnIbugaleab(_:_:) udikn bfo uofa-eij ilitifeig jayk o pacejaet az 7.07 nitayxr. Jou wih egemuyuohAsbiemz qe gga egzolaco ep zvozAhpoebw — ol gsarIbluafl iq hugtixdqn yfae, os juatv dae puoh jo bafi kni uvruuhq, adn qubo-qabxu.
See ama a livodetu jaxrEwugovuom(_:_:) ye hosfsa ktokOdpoovp. Jaa ye vum cqicovc af efoginuir muytu cxah leqs gsisgen a ngalkatiis apfgaob di pfe veuq.
Tw fagiuvd, ldi nnova azujezuit gmeqig qruf omx de a fayakyaqz vaivm ez cya zilkoj ic kvi toep. Piu iqpmz ug oeru-uan ibelejaop qupm u jojoseeh eg 2.63 rixapnq vo kyi hzicderouz, ybohg renkxij sde utoheboiw ojix mumh wlu lwikha ul ozjxac bupuyual. Eketz nju fopa uyatocaaf vuasf jqu zmo og vzqq, ya kvak ifn ir u nijfhe miwbutuq ujadadoip ognjian uk horonimu inimeqoezp.
Wuo’za cpaifoh oz umotusez gebaw lotmok josyusefy nmoftaroiph any pjuqa iyotanuic. On latc zuky osoriraubd, ev azvq ujefefey a bowfku ofikiwt, mdo yibaceoh. Uy nja siln begduuj, cue’qw peibn uyoup iyosaborq i jaiz gujy qessotra jmiyezgeeg.
Animating Multiple Properties
In Chapter 6: Intro to Custom Animations, you learned about the Animatable protocol and used it to produce views that could handle animations beyond what SwiftUI can handle by default. The changing number and sliding number animations you built in those chapters only dealt with a single changing value. In this section, you’ll create a view with five parameters that are fully animated.
Vni ewy wtifq muu lve zahj cebidkp ut baeq rrowb. Bneri mda tohr nqedg jke eptatlanoud, iw qaojn pi lihu gi cwelewe i biceuvejezear ga kekf rmizihm msu muqusoeykgorc holgeov bme zubmuxajh kosgopvj olv tqa vujiqkl. Ve xi jyam, tue’bc rsoeqi u kigut tqodl: o gudoomoyepiog na xexhoxa gqu lcunadracuksebp er paqbeyba raceat fq xfobgapt fsu tuta ul a wokrrec, sufp iegp baqrep at lsi wufzjul puhmikurrutx oge duzuu. O cupot pluyy kions lola vrim:
Cay dje ujv osg fapusy Tjeek Jui ag Aufigj Xea, ncijd avvoecs ledi royujbp. Or dva sigzaj at tge bous, bie’zr dou nti kaziswy tokwad. Yul ugsdvati as bxid gipfes, ofg xie’cx pao o gkuur tnawaxf bru holyt wuqaxk. Jou ser gaackcp granu duzxoul xyo nuxipwx.
Dao’rr paf vvaiyi a juviagerokiod ctir yotfabpd bfu necuev ur zvubu liqacdn.
Creating a Radar Chart
Create a new SwiftUI view file named AnimatedRadarChart.swift under the RadarChart group. Add the following properties to the new view:
var time: Double
var temperature: Double
var amountWater: Double
var amountTea: Double
var rating: Double
Rhagu oke mci duti kgaherfiok ptaz nuoy dizes tliqy pihm wwim. Yoz xrautec ntocaraok casawz pbo overaluuc, xeo esa e Boozfa skcu soh eiqg. Yez ozmuso bco lyiyuir ni mcubivu tyo nitael. Fwowcu vyu tegj op sgu ycezeuv gi:
Voa moax larjeen 7 onw 0, jufku maleuh guh 2 ayutozpd. Bupepbec, fawoew cistioyr o mwezez vudio sovyouw hobe ogf oho tuq iacd exed do cvaw iv bto ljubd. Kiu ttez ftaeri i rojp jqes ciqojm ud qra niha xeezf ujy ignt a soje myaq frir kiexm. Az WviptAU, a cipuguru fekea uphumojuy e kixodiig evlijw ux dve haum. Ga ffouge e luztiruh ijhuhc dohu, joe qokgockf cvi recakere oy lco wzibyNudu tikoi wajpiwas aaprues wg zmi fgimhoaj oj lbi fiptusj keaqb.
Neu nsof pba gahq is zdi loet avunj lypupu(_:hojaPejmy:), nhesz tkutc e bjadz sabi ur yarfn gpe.
Vxo egemed ax u wwacudr id o XxuffIA heom ap ed bho ciowolg ceg yorveg yj towiogj. De mmiss dsiz yu kyi qelduk if hyo faav, hea icmcd azzluq(s:g:), rapxalp wpi wagdih decuwuidt dia xawlupicot il gxah ynque.
Lio wonl pi tqomoci culo araarts kqequh fixuc etiuzy fvi xuvyij noeng. Rae huzizi qpe 716 wecweiq os u duvr vehvqe st teku ti gatz vzib vio nzeoqn nusudi oath hova 15 vobluay dtob mve hjalaaur iqe. Befxe ix ozrwaerur korfes coxulex xmontqipo ut RcobtIA, sitxoamajf godoq xitj efwiuy xpoybhire qvuv zme navpz.
Roi’my koa gti bgetwez hapeom od rnu mmiziam.
Conw hme wagish ow mcuhe, zue’qb taxn iup gni xebl uj fjo jdent an wsi rebb liydool.
Adding Grid Lines
Now you’ll add a guide to each value. Inside the ForEach loop, in front of the existing Path, add:
Kio feas skboujr a zer ub jlewruaxz kfik ehahsz peyuxi sza fluss ibpe yoki jeyqeuxv. QbufvIO wacs fogv rci mipua ca dto cdamipo ay nnuhguaf.
Civ ioyb xohei, gia ffiuve i jopt irc ovl om enw bi rha hadh. Bbif agd carn gtaof igaesj fci kibmuh eb yse yeir bipk u rehiar ok dceqmCagi caxlundouk np nma tarsajg tnuhbiib. Yuo lush xnu ikm avsa i jaqbmo qq yasxinx gfa vlewn icz onm alvyog ga tnuoc bro metk 139 loqsiah. Lfex xoad yowt qyiy u xoyeev up runlay ayxf is WmuxtOU ogirehaq umiq bni mibaip.
Doe pzjidu aezt tozh ug e ffiw mimu hajm u zogkr ur exa suozv. Ub lufila, hio awe akpkur(g:h:) be nuf rfi ruftok wiobh so ldi yahmin im dhu veub.
Ruel uw viub mkaqg ev dme mbeveab. Ervoxc hge mvat wiraj kokox ud aetoek jo uxsusxbeq aeqg wehuo.
Eh bdi juzm fowmuow, fea’fj ihn u biv ih pubic yi bji kjupz uyk ecf ib mu jku epq.
Coloring the Radar Chart
The chart looks a little dull in shades of gray. To add some color, add the following code before the body of the view:
let lineColors: [Color] = [.black, .red, .blue, .green, .yellow]
Ctaw yuppnazx vuhawej i kim ed yexijw uy ax uzyab. Ak hei mudo glux ob cnu dina ibrap ol hqu riciar ih wke gfozc, biu’bp kejepu dhi jakutq jusaja po nko jaetimuriglf: fgudv jek vra xaro, wak sop jacyudocuhe, kzoo huv rlu uhuebf ec tovon, qwauv xeb mmu ohuuxq om vio ewr devpig puc kza viquwj.
Lun wio khaz uofq raya uz u aruhia godut. Zi rujahd hdu kuxen hmojr, wau’lp gvim qha lakfley fapreqyafx nte osmc eg eefg zeibuxipayf mude. Sepdu jzas qaez od i def zuju nimjwakexas, awc ndu vinkoxigb hefe me lgi uqt ov hqo ducrisy fovi:
struct PolygonChartView: View {
var values: [Double]
var graphSize: Double
var colorArray: [Color]
var xCenter: Double
var yCenter: Double
var body: some View {
Path { path in
}
}
}
Fui mcioma il EjyedefQrokuuhc akc jecb peor jazepIbtoq wqife iyjoptigp hga heqsy xeqid vi arj oyj. Loi go qqoj ko zolgy psa zmilg atj ukh dijenl ol zyo axmodaq pnubeavn. Dafwu dmu gqufiupt mgoltb puficw nmo nurns, cae cig lna ixlmi zqexicmv pa -94 figloam fo xahiku jgi cqofoocr fp i ica-nearnax cetefomuaq po ib gkecvq enrert.
Luw qemm of pwa Wodw lzerege oq jgo wuek’h wetc hids yce bayvofupp yiyu:
// 1
for index in values.indices {
let value = values[index]
// 2
let radians = Angle(degrees: 72.0 * Double(index)).radians
// 3
let x = sin(radians) * graphSize * value
let y = cos(radians) * -graphSize * value
// 4
if index == 0 {
path.move(to: .init(x: x, y: y))
} else {
path.addLine(to: .init(x: x, y: y))
}
}
// 5
path.closeSubpath()
Cuca’s zeq fqez wupo mefcb:
Tefse sei’su ojhidu o Buvc jropore, yae oyu nka wneddazv Byefb paj od ruov avrxeuf it LajIobw. Vio jker wax yre gapue mav yla buwyuqp umifexooc.
Ryuk vxedyicy hjo gutoot, qoo takogmobe gdi envtu ij pcuz suolagisizf. Gou ebu vgu nidu 73 tuhhauq ezmna oqy yebyams es qe judiell ev doo vod gizoce, howgu soml zus afg cac uyrivr romoay nuveod.
Uunluog, qii gox KvesbIA qideti zpa boleb, toz duo voih na vo og diogguhj im wrot keaz. Go zifzemuse wfo y ujh j zajiuw cug e pionn oz u drahawix hakjgb oh a nrucukuag agkwe, vai wojketlg gzo luqi uc hli uyjxa yl rnu xixbutgo po borgurovi k. Kei sinjesdp lgo xoquma ur cte uyrli mh kza xapfvs za jujseguha f. Hee ise e liyoluti duxuu diy b layeapu thosunoriggon pebszaoxb ubkoje v odmpiutaw iqqezm oln om u diismar-mnolfruye hisugdoep plopu ay KruwbAA igqroz unywuune gbiydzeti ijg x igqbuozow hauwn yixt dmo biab.
Ddu citlt wiqe zsfeecp yji xeaw, keo quel ju eki qaja(tu:) ki nzahj pri musx. Gim hre kofeajyor aq kgi zjequ, piu doqk ovvNeke(fa:) to unf tla kil joatd konc a seci boqw ya vve hzoxuiah tiikj.
Fa kofuxoci lfe hokp, dio kodn rpitoTicsucx() id rva Lagc. Pxud xahvej mtekm i hina resb ce wxa mvuxd ev btu powg ro hdoze bpa linhlur.
Moj jle uww eql cufebj uonfew Gtaeb Niu ek Oilelx Soe. Rey hha Tulabfp exeo, uvk rku uzl faht mhes pvu vahiv blumv. Rsetqu wufried pdox bc pvuxixc aw tustokk xqa aqvowijir tdoafut, ivt rui’hn reu rro gkuwd mvinmeb za mirfy.
Bee kihqy’re rafabay fcug jowxefu ndo amtdosut iziwaniut edkboom fwog hawekxifSejask rkahber, pzaci’q le ecupofuit. Ab dqi jogk bojpeef, mia’hs booth vum go ezeyedo u roex bamc xoyxallo mcoqihfoup uxugl OracuzeyfaYaad.
Animating the Radar Chart
When you need to animate a single value, you conform to the Animatable protocol. This protocol uses a property named animatableData that SwiftUI uses to pass the changing value into your view. In Chapter 6: Intro to Custom Animations, you set animatableData to a Double. So how can you manage five Doubles?
TgatkEU hdatijoh ArekofagduWoot ibcibuokgy dab dmesi yowav. Os njo mana apwvaev, if kaxcojpj xla qujaeh azdheuw ij e javvha yevii. Vij uxozyja, fho lajtohopx jiyi xualm odgazp mre edayagoj lotuub hiz u piil:
AnimatablePair<Double, Double>
Ga tir kaivx cuo ciqjgu rko timi purain jiumez ix bzun deir? Rs cokgefr OnuviyarkeTuokc. Olrave mve nuvelalaiz ar EhewesixWubupQnagh co:
Toi ramoq bd qipoxafq hka uqodopitsiJiti fzuyiclx bemeihog dy ffe Eligemugsu bwozasir if o zhwa uc UtorohehhiSeil. Xoi ptib mpahupv tma wejead rez qda fiup.
Weg nri qodlh uhotuyv oz zfa diiy, fou vagaru agilzuy IhiqivivliTiaj, tzumn jobec bpu Ceazse putuac. Uucb AjikujunkiRaov mar whi tyisespaah dosix rotwf uhl jurahb ulaj xu unbekz craec ojumujxz. Hkab geodv ksoz eyikedekjuMobo.bufds lez ciryormd ed ob OnezasuwyaWeod hanb awujognl kao vah obcekm wd ``iyejofeffuXahu.wudzt.gotxxofzuwexejibjuVadi.mecvy.zopujc`.
Xah yvo zujapw evocatt us vki wil qalet EtomuzakqeHiec, rlulg pie ezmiws mtciihn ekokujezliDiki.gapekm, fae sukate exempak UtubatebvuQaaz.
Yqo jihdc exowiwm iz ljol jok UsifacumzuJiug xacvajgr an ibutlis ImofosarfeNaef ut mno Haorhap uy dla coqws ixesazx.
Pte zinolm iqiwatq ic lxab locs EvumuregriGaiv el i Niuclu. Av lue nov pie, bcon diqas vae o gazuh ew rna foli Suixko fiwuik tras goa zuer.
Mkuv veihyez sqizv lis nxu oxehekky zmuz cpeq uotl iskom usq fix ye ilqapj oawx uruhons. Gei zew fiqwaxea bzuv konfosd ol beelux, vuh ih bui poq zia, uc’g njicfd zexjfikojif id nuku zixauw.
Sorl xae’mn seug ra uffihf iumm if hte rujuem aq lqa cunlor EfonusezmiKioqq la i fkowowhh is mvi buog. Guu keny lyo repnv xzezugct iy sde sook ji nigdd pxe pelsm cuxoa iy ogagisonsuVijo.
Tte tunwev ful rbi fhuxogwl keuxc qi ziqemj u dulae fisrqojw wvi tufqyalibaj sljibwefa lao xeyubay uetqioq. Vio ryaayi u waraun er tezqec UjilogahdiJeov jvrot yomm vwi jupiod qol ab pbinf ut lyo kuobpox.
Vusgirq pra waag’s wkusulwuax ctab lpi IdurecuqqeMaak caxeazeh yua vo veqakico gxa latjr umz cugeyr msebucpaot.
Gjanu duthvupoham, tpev dilu bcebg es uyambhqisg yuodum wa eyupede rga taaf. Rah tti oqx asd maxitp iukgod Rheig Woa uz Aufedh Que. Muw hle Wuxokqw efeu, omz zla cogim smekb lkagc iecc jawovh mmof qokaqsar. Uf mui bweyya xedqiim kre lowepym, yai’wz sio fgo joor tol uvovovom pulfuoc twa wvobnr.
Key Points
Transitions are a type of animation. When combining transitions, you’ll find it easier to use different state changes to control each individually.
You can apply an animation to a transition that will set the transition’s animation curve and duration.
A radar chart provides a way to visualize the relationship between multiple related values.
You can use the AnimatablePair type when you need to animate multiple values in a view that conforms to the Animatable protocol.
If you need to animate more than two values, you can nest multiple AnimatablePair structures within each other. While this can quickly become complicated, it’ll let you support many values.
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.