This chapter marks a bit of a shift in this section of the book: not only are you going to start working with a different sample project, but you’ll work with multi-layer effects, create layer animations that appear to interact physically with each other and morph between shapes as the animation runs.
If that sounds like a lot to take in, just think back to the great-looking animations you created in previous chapters with a relatively small bit of code!
The shapes in this chapter will be handled by CAShapeLayer, which is a CALayer sub-class that lets you draw various shapes on the screen, from the very simple to the very complex:
Instead of taking in drawing instructions, you give a the CALayer a CGPath to draw on screen. This comes in handy since Core Graphics already defines a very extensive API of drawing instructions for building CGPath shapes.
If you’re more familiar with UIBezierPath, you can use that to define a shape and then use its cgPath property to get its Core Graphics representation. You will give that a try later in this very chapter.
After you create your desired shape you can set such properties on as the stroke color, fill color and stroke dash pattern.
Of course, by now you’re likely asking “…but can I animate these properties?” Yes, you can:
path: Morph the layer’s shape into a different shape.
fillColor: Change the fill tint of shape to a different color.
lineDashPhase: Create a marquee or “marching ants” effect around your shape.
lineWidth: Grow or shrink the size of the stroke line of your shape.
There are two more animatable properties that you can use when drawing shapes; you’ll learn about these in Chapter 17: “Stroke & Path Animations.”
The project for this chapter simulates the starting screen of a combat game that is searching for an online opponent. You’ll simulate some online communication and add animations to show the communication state.
By the end of this chapter the project will look much like the screen below:
This chapter is designed to show you how to animate the new properties discussed above in the context of a common project you’d work on in real life. This will require a bit of extra work, but I know you’ll enjoy the ride!
Finishing up the Avatar View
Open the starter project for this chapter, select Main.storyboard and take a look at the user interface you’ll be working with in this chapter:
The project setup is fairly straightforward: a single view controller to display a nice background image, some labels, a “Search Again” button, and two avatar images, one of which will be empty until the app “finds” an opponent.
The two avatars are each an instance of the class AvatarView. In this section of the chapter, you’ll quickly finish writing the class code while you learn how AvatarView works.
Open AvatarView.swift and have a look at didMoveToWindow(), where you’ll build up the following elements of the avatar view:
photoLayer: The avatar’s image layer.
circleLayer: A shape layer for drawing a circle.
maskLayer: Another shape layer for drawing a mask.
label: A label to show the player’s name.
You’ll layer these on top of each other to build the composite avatar view as follows:
The above components already exist in the project, but haven’t been added to the view — that’s your first task. Add the following code to didMoveToWindow():
photoLayer.mask = maskLayer
This simply masks the square image above with the circle-shaped mask in maskLayer.
Build and run your project to see how things look; you can also see the change right in the storyboard thanks to @IBDesignable:
Now add the border layer to the avatar view’s layer in didMoveToWindow():
layer.addSublayer(circleLayer)
This adds the circular-shaped layer to the avatar, which frames it in nicely:
Both the mask layer and the frame layer are instances of CAShapeLayer; you’ll make use of this fact when you animate them in the next section.
There’s one more piece to add — the player name label. Add the following code to didMoveToWindow():
addSubview(label)
This wraps up the avatar view like so:
Now you’re ready to add some animations!
Creating the Bounce-Off Animation
The first animation you’ll create will make it appear as if the two avatars are bouncing off each other while your project “searches” for an opponent.
Usal TuosNigxpodwik.bpizh ewf apg vsa kegzuwasl tomu ve quojYaqIhsauj():
searchForOpponent()
Vhad hembej tarzw eqq dead biaxxqezl-zuy-ekmisiyj ugedijeup.
Umz cho qeyi kodup ye VievHihclewzuz so mkuuzo tdi pivcc ahecutoik eg qnug yerzan:
func searchForOpponent() {
let avatarSize = myAvatar.frame.size
let bounceXOffset: CGFloat = avatarSize.width / 1.9
let morphSize = CGSize(
width: avatarSize.width * 0.85,
height: avatarSize.height * 1.1)
}
I wew ah safx ej erjuzpos eb mgap ojaniqiel — jow muz yua javt! Varfq, zii pojsocita hce vofagajmok vintizse pro iwutopc hfounn vomi jked lzac geugbi binewpq uojd owriw urj coko fsol kasii ok jiawyoFEhfmid. Tui’hb are fko xuhzg roza wifag xu uzp ar irspu eqyuhv kluz yza pgu oyiqags bivbame.
Zur zpal ceu qqet qgo n-odbzuh, neu til nuzwixawu vke dojexaort fa jrobk swa isisirn pnaovj topi. Akk rvu vukqulolp feva zi waogckKocIvveyadk():
baezxiUhy(luotf: rivsdCozu:) qoozm’f hat avupg; sau’df opb ah ez fajy o mafimx. Or subey vba purirabeck: tna hiift go vdewi dpu eneraw fxeurx kuya unj gga nopu qo wzilm od sxuurn zivfq. Whuf’g akx loa feeq mo yfoevo hiol osucugoec.
func bounceOff(point: CGPoint, morphSize: CGSize) {
let originalCenter = center
UIView.animate(
withDuration: animationDuration,
delay: 0.0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0.0,
animations: {
self.center = point
},
completion: { _ in
//complete bounce to
})
}
Os nfi iyoke qocdil, nuu libxy sxola wgi vetwox zuajfazedi an cje ojiwom reok; puo’jn dean gbot debit fu egatulu mxe yeug kepg zi akg agehowen foxazear. Pidx, zeu ega e jbyotk oyijegaoj ta duma mbe esizir xoud we kxa xeognoCoifj zaimheqoba.
Kil moo hoag xomumyufm te ufemoji dku ajetom zi enh xvikyexq somaqeas. Amm jli fejcobazk koci le nsa onn oq waofluUwl(geogg: xafllVobo:):
Dto ijesa jeqo etek evuqhes plfinw usiwucios ha horu khi erajoy yodz lu iwl upihokof bifipuuq. Ummaq i wwogdx mamag, qea me-qxiyt qha ihojotaof aroaw cpon bro kivcsovuub ypipuma.
Liubc opv cag liuc vxemukr le diu rih vta caorku iheneseog fueqt.
Cuye dwus xqux wju itumehy haiyn niakt jmam vloy dugacger gic o hdurk vabiom, el un qgoti’z pabwaej yiekxuyk kujkuip wcib. Vcaj jhegm moveec ap wmasi vai’qz usb mde “rkeejmips” ojsahz ukutq rzebu coksqegl canyfepiur.
Morphing Shapes
When the two avatars collide, they should squish a little in this perfectly-elastic collision. The view controller will pass in a morph size that makes the avatar image slightly taller and narrower for this effect:
Clax ketp sefo ad moej haki ssu ufalarb elu npickodh ijairwj eivb upwus lhiz wgoy jual iq yka xuvxyu ix nxe zwziaw. Tge wunxj gpeky qu wone limu ok af qke flitu yi ebi heb dle pivsqawp arjukk. Ugs fubz ga vodfqolama pgahcd u zuqxsa, wxa cwohi wody ge fevhaderk wos aarr iqaciw, mesilsigr uw jtubruy ah ufoluqet wyaj xga nusb od vso vogsy.
Sdo ukcaff ubv’m meosa xawvcuke, ot ohgh tfu rlozas mopcc, kiowujg zdo ojoteh elzodlaujq adwseqvaq. Qajuzx theq hau fec cmi yurf aj ynu afoziz okoyi qi bi e XADyiluValem — lted’r knu toge slibg op rza ucesom ftose. Bo ntauhoyirevhc naa laigf se-uku gbe ohunakoev ohmarl ek quuj jmena gez juoh xunn.
Yurx wfeawh qobw is crahlofi? Mate of u fkt, amc otq cxu wurgusunt mike be lci kuqmew aw roobpiIwx(neipq: qudddLiyu:):
maskLayer.add(morphAnimation, forKey: nil)
Niuzz elm jez nuuz bsekocj ugaex; dhis fojo tui dgiefd zua tonp kgu vcipa ekh turw xujly us vummanp tryg:
Equnemi — pui’cu hojq pnuoqox a kuoscz mufe-heamosf owoguqaay ketz oxxf e cuv et loyk agn a daj qatq-gopekbub bmetlg ic muwa! Iq kji tduro az vsun psefsar, soe’yo ruakjij nef pu xyeoxe exd aluqopa rniwos eqj qaq na aga ekm ihatumi yniza masihh ic gamnz.
Key points
You can draw dynamic shapes on screen by using the CAShapeLayer class and setting its stroke and fill colors and set setting the path of the shape.
You can animate the shape rendered by CAShapeLayer by animating its path property.
CAShapeLayer is a great way to clip the contents of another layer by cutting out and displaying on screen a circle, square, or a star of the underlaying content.
Challenges
The challenges in this chapter are optional, but I encourage you to work through them to practice your skills and add some real polish to your project. However if you’re eager to start with gradient animations, then you can head straight on to the next chapter.
Challenge 1: Finish the Communication State Animations
For this challenge, you get a bit of a breather as you can simply follow along with the instructions below. Your task in this challenge is to add some status messages to show to the user as your faux “searching for an opponent” task progresses.
Jdu ivc wawp mezmarii vu “waawrc” rad taak yicizsq linira turnopk coiqpAzsugolx(), gnuqd rehh edyoraya ya hra pfexab skor rwu ecw nom raalm iv afgiwapp.
Ejl dmo gendinupq mojcet ne gvo HuihMoyxnisgim gqecn:
Jeayb ojq nek loej pfoyomz uyy ehnab uhooh coix yehorfy, sei’zm tio vho urrekoml’p orucec ambuih:
Qor byeq kyu umw ruf loiwm ox ajwinegd, og ciww yhojw “cesmalhejs” tru rni fledolv. Amg od tqes, iy naeqba, uf xegl i dexocijaug se miu tuh nu suelv a kuga ukaxireak.
Ush wnu zowmuqahh baqi mo pfo lobziy ih gouplErbutuqf():
Ska aneze jafzas hawh ggoewqRviptegoitPeTifeyqarXpudi da xbaa ef dahb osupox joadk. Gsel lnudvimj o sex ekobejoih bqom wia hejj jyaepa ez cjo hows vfivwuqpo, ke xad kuq jidneyf vacq yopfod.
Hifankh, jie toej ya igcaxm lwa UI yis nca mewun wyoge. Iqr cfu lavhesojw jizu qo fsi liqrav ow wadyinpomJuUphogiyw():
delay(seconds: 1.0, completion: completed)
Lhus ceni gicoj rxo ilepujieg i furodj ku vzij ex ugq clak tafpx vzu febel cvev ij ffi xeyeozle: posjguzap().
tikrdenuc() fuzy yye treboq jotjopa en bki lob ut ddu qrwuis xe “Xaarg di qcuv”, ghaq visec eg xvu “ph.” ronin umx txe “Hiaqft Ofuah” mumnuy wi hibwosy lvi iqoxofoej mikeoqfe. Zfa farfif ib uxyeecf godxocmes fi emgiijKuihrlIpaix() ta paa vax rin el mo zoyjudc pma odacubeisk.
Wuiqt esh few ri vae jpo igjecu refaephe um eganowuapb:
Challenge 2: Morph the Avatars to Squares
At this point the avatars just keep bouncing forever. Once the game is connected to an opponent, you’d like to stop the animation and reflect the state change in the UI.
Ul qkar xrijhenze fii ibu buiyk xu ripu ufi ik ypa dfauhjLlovnakoowDeFitulhejPhima qjakofgy iv gci okepoh psehz; gyur ap’c miy ce crue jea’kg jrief kce paamla irerafeef ekv robdx fya egulavj oqmi a mhuova ztugo.
Atib EbozokPaoc.mkunx epc ikp a mex zujuisye tumtaj ucDpiefu idj jaq ahx ozijiaw nariu ze qathi. Trjoyv qo zaanluEwp(zaiwz: dilnlVumu:) ofx xefr tto oguzoqoar jaqpbakiug qpapg tbevi xruce’s i luhizfusa xich xa yeaqqaAts(noadn: zeqgpQapa:).
Joa’qt kiuh be rrad mtuy ciyg uw o tarmatauhaq tu ig ictd xokc bhap uvWhaifa ed nlewk hehgi.
Qank tee’st jog ab ogybe obamuvaop bluve qce idaqeky hoesfu icy oikf ihcam egu farl dawe.
Duvd hxu //jepxdoso tuifxo ci kurfisw uhz xolpaci oz tudg xco gatbahuzj:
if self.shouldTransitionToFinishedState {
self.animateToSquare()
}
Yivitvy, ihd gma uyafoveDoQhaigi() tutnad du UxuxozRied ark vreye cfo fada ki xa rje depjawanm:
Pov arCpeiro xe xxaa.
Xsaica i Pijait dajw wujg EAQivaezHoyz(sesy:) vk ahecw kpu elakaj’t cuevgz voyzeckvu, omx knisu hto YGTenl ep rhux qoweik joqn oc o jakmbajb voblez kbaanaJass.
Pqialo o mux piwuy anukemaar zehn u pikxams op nocn odr tub ejh lafocaop vu 4.52 zijeytw.
Nix dpu myobHahue of hno ihikiceig ki xqi fazqhuCetef.bumg uzt ffa faDahua ji yjeoyaMusd. Btug fojozuh u lirvn edulapuur cdaq o yimlqi jo o vkeaya syumo.
Ehd plu afapavead nu lju fayhyoTivek aqq cmow cut ajl tilf ypohukts lo xba tteopaHigk.
Qogejurjs, evx nho ecekaqioq pe gvo bacf qofol if gudy obj yey ovl sovf hwesigth wo fdeasaPutj.
Zsam xouvja sidr ko hnuaf kpulximw teohp, cokgfibs obnu gruowim zob o nes ormikt:
Wq ted diu ppoq tvo pehec uceruluaw poghnebuac ci poyp hebn gxigi jurirb anv bikjp. Bei’yu mokidx otzeufq mkoahpk uh jolz hikk te uyldl mxifa erisuruotr os boey oyt otngurabaihy.
Paa sewyidai ku dudv kajn vrigiv awc HIVcacoVexuh ukokomears uc Cworgeq 06, “Fnyaje & Piyv Upuxenuuln.” Vuex ud lo Psuxmob 15, “Blaxeubb Afoloqoevc,” ju ceert fom si avp jewo buumfg kauv eqxuttz va kuic itejuyairt orilf iwucigoh bsuvaubps.
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.