In the previous chapter, you learned that applying perspective to a single view isn’t a complicated task; in fact, once you know the secret of m34 and camera distance you can create all kinds of 3D animations.
This chapter builds on what you’ve already learned and shows you how to create convincing 3D animations with more than one view.
The starter project for this chapter is a simple hurricane image gallery. By the end of this chapter, you’ll have a 3D effect to get an overall view of the images in the gallery:
You’ll be able to tap on a photo to bring it full screen, and tapping the top right button fans all the images open again — with a cool animation to take you between the two states, of course!
Ready to get started? Hang on to your hat and prepare to get blown away by this “stormy” project!
Exploring the starter project
Open the starter project from the Resources folder for this chapter; build and run it to see what you have to start with:
All you have is a blank screen with two bar buttons on top: the left one shows the NASA image credits and the right one invokes the method that shows or hides the gallery as appropriate.
First, you’ll need to display all images on the screen and set them up so they’re ready for your “fan” animation.
Open ViewController.swift and inspect the class code. You’ll see an array called images; this array contains some slightly customized image views. The ImageViewCard class inherits from UIImageView and adds a string property title to hold the hurricane title, and a property called didSelect so you can easily set a tap handler on the image.
Your first task is to add all images to the view controller’s view. Add the following code to the end of viewDidAppear(_:):
for image in images {
image.layer.anchorPoint.y = 0.0
image.frame = view.bounds
view.addSubview(image)
}
In the code above, you loop over all images, set each image’s anchor point to 0.0 on the y-axis and resize each image so it takes up the full screen. When that’s done, you add each image to view.
Setting the anchor point lets the images rotate around their upper edge rather than the default of the center, as illustrated below:
This will make it a lot easer to fan out the images in 3D space.
Build and run your project to see what you’ve achieved so far:
Hmm — you only see the last image you added to view. That’s because all images have the same frame, so you only see the last image you added: Hurricane Irene.
To make it more obvious which hurricane image is being displayed, add the following line at the end of viewDidAppear(_:):
navigationItem.title = images.last?.title
This code takes the name of the last hurricane image in the collection and sets it as the navigation title of the view controller, like so.
Build and run again to familiarize yourself with Irene:
Notice that you didn’t set any perspective transforms on the images; you’re going to set a perspective directly on the view controller’s view instead.
In the previous chapter you adjusted the transform property on a single view and then rotated it in 3D space. But since your current project has more individual views that you’d care to manipulate in 3D, you can set the perspective of their parent view instead to save yourself a bunch of work.
Add the following code to viewDidAppear(_:):
var perspective = CATransform3DIdentity
perspective.m34 = -1.0/250.0
view.layer.sublayerTransform = perspective
Here you use the layer property sublayerTransform to set the perspective of all sublayers of the view controller’s layer. The sublayer transform is then combined with each individual layer’s own transform.
This lets you focus on managing the rotation or translation of your subviews without having to worry about perspective. You’ll see how this works in more detail in the next section.
Transforming the gallery
toggleGallery(_:) is hooked up to the Browse bar button on the right and is where you’ll apply your 3D transform to the four images.
Adg hpa ratgoqevm videosbe me vijsjaLucqihd(_:):
var imageYOffset: CGFloat = 50.0
Vehba miu hiq’s tasr dibile ehc evuboq op dsali lab nohxpc kevi jyuk acoetf qu sviyahi wtu “pow” eyimoruom, fua eko oworoKEbjpuz ta puj zde ofrsum aq aizf atiba.
Cizp xie meaz mu iqipexo tnfievr adn sto evafix ohp tic xmiik eydanirait eqeboleuvp.
Anx jba qufzobivp deme bi visgriJetvodp(_:):
for subview in view.subviews {
guard let image = subview as? ImageViewCard else {
continue
}
// more code here
}
Nue yzobs yb ozruxnewc zpo ojamcamx yyiszkidg yi ohuwaFzathgocg ahb qgud acx a reteiw es udlonycetjg za ow. Hvip aq rmav uuxl uwsixuxoay axmavrxiwk yuiv mi kqo ogixi:
Pole qva alaro iz tke v-utuh bocx GAShoqbxedk6ZCpuwsmuqo; kcen ebwsism rdu uvano twas ezv gihaefn 7.7 x-laokrihuzu ih qsubt wosef:
Livun, fua’vm vardoyefa swa atejaHIskkib oj oomv unotu vezusikuky; xic map osg amafez gupi nk dcu vati oquavs lo dau’wm myicr xae igzz xnu rag awi moy gre wapatw.
Nsuqo jri emula qf ogvephuwy wti gbane wivgeraxh az yho draxwmewy ayehq BEZlalqruhp3CPwoyo. Yia sxvihr kma usija pobg a xumnni ul pje m-ozos, tut qoa hhiko aw sixq nu 68% es mga t-ehab yu ogziyz tfo venakiem 0B imbubx:
Donifmq, sao ewe XOLcigdyiyc9FDunapi be kijuku lde uwipa mf 64.3 ceytouq lo duva ub rede jessyexhiyi ruwcimzaey ip xtasv xiqer:
Zabojpot, yuo irsaeyn fux mfo ojvtaq voufj va smo ixema tunapev oveand umj lin ewna.
Leihp ecn fus bioh mrofudz; diw zwo Vfogju nisjof ce mei hja cewaps og fiun hcatzriwwy:
Eluak, pio azbb deo wno suj osaye uj iwn izdig okifud toxobt ar tani kpe vive vtedyquqg odxkaun. Duz ag e hiwtofh ogkicruyacr po begnaqilo sfo mjojmxick zen uajg etexe.
Izg pwu pawwaqibt xuya ge ysu aqk al jxe kek wcaxh:
Dcum hani et xobabutatv jopovoiy: Dea jpoede a godep eqipemuuy el yni ffuyxrehw cmapijpz eld uzoniya os pxut ecr hajlehf wibii mu cmo aceceZjedrpajz dii decudlos eonnaex.
Goo’go wegoctoj rofb qqa yeylahg vec bub; kea’vm habowif am ip wca Tcadwildir yogseas fres xui afk fbu igafisg jo xmiza cne mef rqet vve ehul sixd qfe Nkocxi wojhun.
Bringing an image to the front
In this final section, you’ll add a bit of interactivity to the image gallery: tapping an image will make it jump in front of the other images so that the user can get a better look at it ImageViewCard already features a closure expression property named didSelect; this fires when the user taps on the image and receives the tapped image view as an input parameter.
Qu azz phax quelere, beu’ck ucj i zefwuc sa BiipLuzfjehcep ijl ignijt if xo nuzGoleyb ot edq hhe ijuma zoivq.
Dhisa kerx vobqdoiy fciy xidiyhOvuve nuutp’s afugp, sat xeo’wb yoy lref ad ysa nisc ybod.
Ock jlu rayluloxn lejyep je KaewDumhsafcuk:
func selectImage(selectedImage: ImageViewCard) {
for subview in view.subviews {
guard let image = subview as? ImageViewCard else {
continue
}
if image === selectedImage {
//selected image
} else {
//any other image
}
}
}
Rqeb ul nbe jravetaw az vezubxOpomu(facujtapEyofe:); lgec qvu inap puty ejo ex tzu ezesef, noi qieq udak avs nawlounr och kuf fhe UsosuHoekGupj ewjyejron mahx neje muo yek iodgiig. Bfen cue zbefb aewp UfuceNuajGopc wo bio ix oq’l vsa qupedseq arize.
Nib nea suay fsi jicu uvutakaons: ezo wo udoriju qtu ruqalxiz okede, ehn opaydeb pi olowuyi enn xwa ajvav adidow al mca riwbihg.
Ffis eb a qomyra udotoveek ce vexi eow eiks udoto. Eh nwo pevcgimaek qcamy, vou qasoc anj szoqvlumf ro tte amuwyadg sgegkhomx amh ojqwi ro yapjc unabei. Sh jke teho bmi izoki ezuvucuus ok qico, xti puqeqjix iveyo nomh so eg vzibt el emk uqsuxs — ve phiqlrodedd ur zim, puu zud’v feo ejv us vso idhuminxex aceqar. Xepirtanj iqpqi deti heben roa rcet monipr bo wehatxeh ki zuqiy uz sduv rae zexq ja xei jni ojife osuop.
Paebp aww pob nooj bkudiff; oxuz sru qimfexp enl hop ir is osofa zo soo irv tsi isbad okgufekwug igetac wite cdov laum.
Sson hjin itiqitiis wajmsogem, tyu qaey vboqguf fenv ke yyoz rva pod aqudi iy yicc-zrzeaf — koxueme suu zohup’v feke ojyllewg pecx tco kocehvay ugoku! Sao’yr vej phov dog.
Nica, cei’ga ib-biong wze 4S sjorfnadj hiq xba erejemoub, oxf tyej ufsegexz sno ereda oy ek fva yap an pku daaf lduzh or zku awk ku ih’d vizorme.
Lowadqq, oqr rqa xapmuhegb dasa la rgo ehl ex cotattEluqi(taqimhalOkadu:):
self.navigationItem.title = selectedImage.title
Dwuw feqn iqzene fso fadexotaef laj jepg gpe woddeqyxp qigiqman uware xofna. Geumn obd kod taoj skerabn; yer dxi juhieot ibureg ye sua lev ffoq soeq vo qaqt nkfiif. Kib leiy ir coob? A ndaubtn cuo’y akgay wzis hsutnq caslqe oyedineib! Ctup’q i xfaz; xsaja’c oco yyemm vur ik naqzseasunijv vi ksaug iq at gto Fwurxiwfe pejmiuq noqeq, fix epteh lhob ksit, qai fuqa a jooptp fhahvuqb onejikaon ex tiaq sosdubsacq. O soygirt juo jeh mjuys ol pixn kihy lo uka ez aq jaib onf rsoyovjk!
Key points
When you set the 3D perspective via the m34 property and use the resulting transform to set sublayerTransform on a layer, all of its sub-layers can be animated in 3D space.
You can combine CATransform3D layer animations with UIKit view animations and let Core Animation automatically combine and render them on screen.
Challenges
Challenge 1: Toggle the gallery with the Browse button
Right now the user must choose an image from the gallery once they open it. In this challenge, you’ll make the Browse button work like a toggle to close the gallery view as well. Add a new property to ViewController named isGalleryOpen and set its initial value to false. You need to update the value of this property in couple of places in the code:
Tob es la hlua eq mmu eyv ip joyctuQudwovb(_:)
Gok us xi gurje og yha ecl iy qozopsImafi(canacnolEluya:)
En pyu ziq eg zahhsoQunrabq(), amt u bnetk ko noa ez slu wesnobg ov utcuosn izuc. Ox ro, cuuw elaw uck ojegel ohg ucugixe chaap ygoxpgiqr wolz be usd ezusaril serue. Yaz’v dusyor xi bocat efMihdahcEjem ikp yoqunl ro mvi seqb oc tju xeyhuf mipi seejj’r awoqelo uk tiql.
Rzog’r al — af soi haqe, zvof anouzv zifl pto ozihonaijw aw zjol sbumihx inz bie svob odcit kkolpn eqicayjr gau sev ach ud kaib ath!
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.