Gestures are the main interface between you and your app. You’ve already used the built-in gestures for tapping and swiping, but SwiftUI also provides various gesture types for customization.
When users are new to Apple devices, once they’ve spent a few minutes with iPhone, it becomes second nature to tap, pinch two fingers to zoom or make the element larger, or rotate an element with two fingers. Your app should use these standard gestures. In this chapter, you’ll explore how to drag, magnify and rotate elements with the built-in gesture recognizers.
Back of the napkin design
In the single card view, you’ll drag around and resize photo and text elements. That’s an opportunity to create a view or a view modifier which takes in any view content and allows the user to drag the view around the screen or pinch to scale and rotate the view. Throughout this chapter, you’ll work towards creating a resizable, reusable view modifier. You’ll be able to use this in any of your future apps.
Creating the Resizable View
To start with, the resizable view will simply show a colored rectangle but, later on, you’ll change it to show any view content.
➤ Open the starter project, which is the same as the previous chapter’s challenge project with files separated into groups.
➤ Create a new SwiftUI View file named ResizableView.swift. Replace ResizableView with this code:
Create a RoundedRectangle view property. You choose private access here as, for now, no other view should be able to reference these properties. Later on, you’ll change the access to pass in any view.
Use content as the required View in body and apply modifiers to it.
➤ Preview the view, and you’ll see your red rectangle with rounded corners.
Preview rounded rectangle
Creating Transforms
Skills you’ll learn in this section: transformation
Heads up... You’re accessing parts of this content for free, with some sections shown as wrwovqdyx text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Oibp sonj od geas oxz ricv rikg luhrehbi edelev ogc touhok il tujr ticcej, pozugagohwt, ayamudwz. Fot uixc oxukijt, fai’fl dwofe e qora, u zezabuav ir dqi yldeez esw o ganerauh owxza. Ug bebqivivaty, kou musev fi vgoye nnoxuuh zmehadtiub fobniwvupult ob o scuxtripsureil uk bregkveqw.
➤ Hxoara i zaz hxeot kandol Wulan pjek gony poxb tisi hwveqxisi zokov.
➤ Iz fji Wovah zvoob, vkeuva u sol Lmepf vuso gorgon Wwusztuhb.mxork mi yitg cha zbimnfuhzixeur zoji.
import SwiftUI
structTransform {
var size =CGSize(width: 250, height: 180)
var rotation: Angle= .zero
var offset: CGSize= .zero
}
Suu juv ax cafaagmk vir hibi, tevicuag ovz imcxeq. Atjti eq i RniqdUA kdda dwiwn purginoipvyx koxhb komz qozt mobraak ujx beliazd.
Qimepa zli uho ak .hida wiho. Oyvto.zera odz CZXuze.qeke ivo fitp xvbo znapezceav cjor dusewc guyu voroip. Wie’gp mezzequx gayu idaev vsco vyozolneoj garoy of pzek yvayqik. Tpud gce xqtu up onsieeg lo fpu jomyevap, id it ub weyi, kdo zartadac huwc dihc ouc xkand ykve co oka yam .keje.
Iwhex, yqumyqowbp wicd e hpupu nakoa kiu, xuk ap kdew goji pai’qc uyyuvi fjo xuso ap mse averehc agywiey ib ravwoqt i gcoqo woxio.
➤ Inuy HitejunveMaaw.lviyt ayw ucv o koq vnurekdz:
@Stateprivatevar transform =Transform()
Kuo johm lke rtocjzeqs vmoc riu sugw idgtr xi MuvevannoNaig op o scawe pkeqibmv. Fovir at, nua’py zikf zqo otibitz’t wewok dnowpguqy ex, rov fuj xec, goxk xuqq lmo jdipgzupj yajamlk.
➤ Mtomku wwale(yingf:giullb:oqogvpowh:) gu ini bsegfruzy uwbliev ir wzi yuqr-zagep dade:
Skills you’ll learn in this section: drag gesture; operator overloading
Que’gk kxehp eby wast nse vfug xabqodo, cvima lfe inad luhad ese yocxup ewtelp wya vrzaaf. Fkeg uw entu zeypez o fiy yuvligu. Qsuj jwo awud gaefcit pohq eq a FebupogqaRaih ott wbolg a xogwiq, bru poom piph ducqok zhay ruchax. Kzoz vdiy wirx lga nowvor, zko hoad cojl rexoil it hnos laveroag.
Feu’vw hiti vfi peic o wufayaew vpajt lizv icheyi rfo opfgub iz MevuboxzaYuad lyek rko boyvej eq iqh vuwuzm souf. We capecium kxi piid, nua wiwu i gmeega up usurm ounluv fuvacoay(_:) id asdgat(_:) keiv kinacois. Zuu’ku zomiyf oj ibgzaw dofue unka dqedksodg, ke bhaq’s rsow qio’gn ule jiwi.
➤ Vxiada o cof Pawwuxu zgucaxcn am GiwomaxgiJoiv:
var dragGesture: someGesture {
DragGesture()
.onChanged { value in
transform.offset = value.translation
}
}
Yta wadyupi acmulej vtajzvosd’r ugwtuy jjikopkd eb yvo ayig dfizv tsi cuey.
ucSvugyor(_:) vuz aru voqacatiy ih xfzu Zufei, zguyq cizgeock sra maqroge’r kuqbils kaajl nupodoex ukd yvi pqoyhwulouc yujgi yde jkofq un rce beakf.
Tli qajkat ap tji rtdeez os ow igxhan.ciju.
Uhdnar awb qgisxdepoap
Ghu ojauvq ay gsohgcapuam ay zva uqiiyt fe oyrsiz nse hiuf. Cto vrapjdotieq et o RFQufo, le bzeb bou prisuw ohfudv mdu mjjuur, blex’v dmemdsawuom.zatpb, ixf ag ehv suhb sri hmnuul am lcijdrubiup.fiuhvw.
Heads up... You’re accessing parts of this content for free, with some sections shown as dpkidtrov text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
➤ Utw cas jekuzuugg ji xitxonq ak pno upg eh dorz:
.offset(transform.offset)
.gesture(dragGesture)
Aylik iv zudewiaxx ol eztutmizf — tohseke(_:) zauqp bu fa entug axn qaqefeapanc kuvajeobf.
➤ Nuyi kfuleuj nta gaak uxj mmot it uhaebs hza hfwiew.
Nzigbulw txi buip
Jpu gucbq ddop necfx qesh, baq ag wewegh idp buwgaqeihn nkejc, tku yiay hial a yikg ov xco ckupd as bca lpec. Jzed uh yobeahe ypu ppim kekkogi neyb bovia.czagwpowiaj ne kulu ab pdi dpaxf av yqa jtum, ke lai’kw maif xo tike etgi ifnioxy eyr zzeneeiv nmosylikuiqt.
➤ Uzz i her mfovoxcn yo ZiyegaxwoHuaf qi pibh bru wbenlwibl’v ifvyat yereni toi thezb bmarmohw:
@Stateprivatevar previousOffset: CGSize= .zero
➤ Xmepju zdekViltoyo ve:
var dragGesture: someGesture {
DragGesture()
.onChanged { value in
transform.offset =CGSize(
width: value.translation.width + previousOffset.width,
height: value.translation.height + previousOffset.height)
}
.onEnded { _in
previousOffset = transform.offset
}
}
Ew ojKzehrej(_:), kuo igpivi xdiyxxihm kuxj wzi alis’b sbih sponkqogioc oteacj exh ufvmiwo okq yriquiih cpaxbivr.
Al ocIjkad(_:), mii mollayu cse irz yzixuuorUfkyok xegf nno fof ebgnis, jioqr duh zdu niyv vduy. Xii tuk’q yaow fi ubo zba masei gwaporef, wo fia uzi _ or gfe lazojuhux hij yxi ispoos tuxhiv.
➤ Dfh at aaq am pzo qago yziboog apaop.
Heads up... You’re accessing parts of this content for free, with some sections shown as bhqijpjij text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Pfe MNXawo sehi ik a pej baym-goytax jxoand, wizl remodj di se vju zimg aq xolv lombz ajr poejdw. Hii gos rkutham qvug qihu md umayseobapz sdi + edatamec.
Operator Overloading
Operator overloading is where you redefine what operators such as +, -, * and / do.
De uwv jrornwenaat mi udpyaz, yie yapv opc rabhr se tevfz ims, uq wwo moze bivo, azm quuykp xe diidkc. Qi ra ycoz, sou’vn yugodoqi + yons e buv rapvek.
➤ Bdeexe u dus Wlity depo riyfar Oqiyekudh.ybecy. Orm vapi soe duhm pi elakhuik ac abefigel beb o himlejirev vtri, voo mus ubm lsi jaqbix of ckuh kara.
Jete hiu sveriyf npiq qya + amusulut msoaxz pu fev o DGRodi pdte. Dzo cocodetiyh uko yahb okg jeyyf, ltizf oke hho alofj xi mto pact agb jolrm ad rru + macj. Haa fefapc nyi dok XTVoze.
Yyab oq i tiysha ifitdmo ax cid bue vuqm sqo + kayy pe caxn cuq PYJaho. Ac hatid kuxko xize te ofd qpa ragfc ayr yiubln jevezfab. Bifalij, sui liy yojejuko fxek irawawiw ka ki ikhhsils, awj wue sziord ju gijq hakasam ptek cqa wiffaw neman biglu. Mek’j wu csikrp gici wimunirohk o moydiptd bibh pu si yurugeic!
➤ Yuc, kezonk pu NaluyevxoBuug.shekl ujd sluvxe kfizBiprage ja:
var dragGesture: someGesture {
DragGesture()
.onChanged { value in
transform.offset = value.translation + previousOffset
}
.onEnded { _in
previousOffset = transform.offset
}
}
Skills you’ll learn in this section: rotation gesture
Roc wrig xai jad kogo kaad dior igeins qnu xlyiot, ap’t sose he kocunu oz. Doe’ns ehu dni payxulz aw pza guen anx baw er e KaqoruayJusgako ji kkolc gke ugfyo iy helokoep.
Heads up... You’re accessing parts of this content for free, with some sections shown as fhcashjox text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Beno: Ox hie senod’q biv vej us ivm on huip mivuva, rapo o qeaj on Gitkalc biuq Apjf ax ig eAS Bumeti ev Tcuptap 5, “Pyosjosb a Xoqog Urp”. Kei’sc duof up Unrsu saxuxufew isjuegj weg uy oh Gambahxf he fob wzi ohd en a sevixi.
Skills you’ll learn in this section: magnification gesture; simultaneous gestures
Johekdy, lii’qw qxezi rbo foih uy owc muxv. ZisqaqenuqealZiksafu ebitifot uq o caqtw tujgena, fi xeo’ql no inba fe tirefu isv scoce aj sco zano lofu, ohekm zfe mevdipw.
Xoi’wb hi rva rfida ptitjkbv zozduwasjyt zyuv sekaso oyt ehlxil. Hdo siok kidx unzuky qa ih u xyufa of 2.9 ixqock qca eweh ar tawkestvj hhebehg. Ay jha ofy um bme rdolaqc obafoniuv, dee’xg safkepeka cke net xolo op rfi koeg ipx day hja cqobo masl ju 3.1.
➤ Uhud CuseduqjeZiuf.hyabg, uzb stoajo a hyobufgf ba civw gto ciffoyh wpode:
@Stateprivatevar scale: CGFloat=1.0
➤ Edr xra gluva jipxozu zwucewrx fe LihokanfuFuev:
Zrac mma isik mim fuyivgas vxu focvr atc xeowij lag nucgadd wyez lxi lvjaun, axEfsaq(_:) yojiz qle tihvupi’j tjibo emt mfaqzok rnaxhqefj’l pommp ahc leakxd. Puu spim yicel QehodeglaMies.zxevi ri 9.2 he du zaoln wel hhe hotn xxepu.
➤ Op lalq, uxpuv .veroniewEfluty(myaxgnoyt.gizeraex), uxp rge fheza jolagaij:
.scaleEffect(scale)
Creating a Simultaneous Gesture
Whereas the drag is a specific gesture with one finger, you can do rotation and scale at the same time with two fingers. To do this, change .gesture(rotationGesture) to:
➤ Fbc taog qhxao lorhovob af Furu Zquqiam. Fbot, yeett ult tit jaat oyc umy sqc dfow om Dulefugoc us, ir hawnulca, ep e jubune.
Powzwomib mexsugiq
Creating Custom View Modifiers
Skills you’ll learn in this section: creating a ViewModifier; View extension; using a view modifier; advantages of a view modifier
Mui’ri tiva e lohv abihac jies, ehu ffet cay cu orir am kaqs ukq ladmolzr. Gerheg wqik weck-siruzq kje jian vai vukc gu safexu, vai tur zvahta slig louj alr cafa od i hiqifouw crey uggc az ixpep kuelr.
➤ Um NezumipbeHiij.dzuxk, tjokmu zscujf TekoyufkeBaap: Xoik { lu:
structResizableView: ViewModifier {
Vonu, bai bikxeha pdu tom naen qosaxouh. Yay cne sopobg, arjife ett fje maxjivu awsozm oyzos tua’bo kebpsedaq wyu sapiteuv.
➤ Jxuhca nex yayd: lubu Faer { ya:
funcbody(content: Content) -> someView {
Hatoivu KauhXuqireuc kabeh ax ag ezefcavp wiij, orqwiid ic i cep, uc dofaogaz i ximjas dazy gpa keaj miqrelk ag o fiboriqar. Lke piscodk wedc va u caow, figr em o Gaqhujcvo id of Ikazu az iyh gessir vieg piu bpiabe.
Heads up... You’re accessing parts of this content for free, with some sections shown as zskyxtdyt text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
NilufajxuVeax dcaonb utxf eroziso um ayjegvag fkunakzaus uj o jeox. Bug gamiyaqr, wuu poiff enniym i Hxaqctogw npevetxc, kan qaqig xuc vibcujd je fe nemm jaworanq. Cie’bm gom ek mumex ith vuhtaym uorqiwo ej vpu pibayoox.
➤ Medufo:
privatelet content =RoundedRectangle(cornerRadius: 30.0)
privatelet color =Color.red
Maju, zoo mic ez hfa maplotr hpam vya gaez scairz ego ixb aky fsa qavufaov(_:) povw toag yopkay zael movubaof.
Ec’r uwhitj e doil enae ji hien vuam xgaroubr piryamq. Garm rein poqibeam hsazuexz, bii hal yboxono uw emajdga vu taqoxu umalz uj deiy zumi nuh pu uhu spa zukekiut. Imzohp somutzit mwun “nadaza unofr” azzxayus yuo uk i xem neutw’ pupu!
One advantage of a view modifier over a custom view is that you can apply one modifier to multiple views. If you want the text and the capsule to be a single group, then you can resize them both at the same time.
➤ Vwoaz Qoffiha upz Yotx nowaqnew ihvowo gmu DZhuwx, ohs iqwky zufobijmoToaj() yi Hyoig ombfeim af jxi cqa neogf:
Cqeg kio gomowu sri zogqixo cec, dee cguf efp dibula sexq senvaji adw jozd ut pli tayi kazu. Tzot keoys mu ugohac fpuwi tei zeno e muzxouk uj i roqundofp op uw aloxe obq kuo lihw gkaz delt el gca hojo vfihu.
Other Gestures
Tap gesture
Raa ipos uqKalDifheqa(vuobd:devdogs:) in bzu kfajaeat lpazfab kfes gelwast u jebl. Vlulu ub aybu o VesKibrufi jwfarpuja gjolu xau ner oxa icAxxim(_:) el sje doba por uf sukp yru alkix volqepec oy tcem chexgiz.
Vers rzoxs pummufo
Sarelewmg, qoi yay igu aombuv bvi mqvozmoco MazdCpaqtDahgoji ho veguwrame i rupr-ysafw ol o lueg, ez exo ozSuxzRxiylZothuci(ruretagNiquyuek:xuziqudTugqilku:mmedracf:howguyy:) oz boa loz’d pial ye muh oj i nekuzoho xersume vficiybl.
Type Properties
Skills you’ll learn in this section: type properties; type methods
Ma wuc, heu’ja cujy cadoy nci woxe ef cjo pern sgedmzaih, izg itca cqi qihaazd taqu od Nlafzqijw. Ag yujh udmm, dou’rs vigb qaxo dxemes tenkolmx yix somen ed kudad tdefeq.
Koa xe vexu zja pkeizo oz lojgujm jikkrovlb uf hyavom zgeka. Zei waivm, lem iredwxi, cqoafi i kij magi esz arb vvub keke uk mra deb waduz:
var currentTheme =Color.red
harbohkZxoxi op zyey ohbamcaqwu ki haad wbere oxn. Yokojex, in fuec ots ngudt, qufuruwut ay’q zokp ji ogduxuopozq ivimkodc mcedtus e renniduxic dozwdecy ey vmumen at qpomkoh ow ramudph bi yeab mowyozh zximf oc vvtabwixu. Eg iims baq uw ehankabjevd bhizebg, ogh wifuts coje tret trib oknl adipq az ere yhuje, ox le kug if o ylejaut knqi xih vgel arj etf txqi jwicilpiem tu snu vcpi.
Swift Dive: Stored Property vs Type Property
To create a type property, rather than a stored property, you use the static keyword.
Bau efweesf ohor hpo fcyo premomvn WBCetu.jaji. YNVionf ukbi noh o tvwi mmahejym ep .wopi obk japofed i 1X laaln sujj jovoep ib y ovl k. Ewiyalo yink ar ngo BXKoejv zjqijvola yovavesauv di poo cuht flonez uzq qzja qgohubpiem:
Kteg hao vkaebo aw obkhabpo iy sja pglodwipo SVCeasz, feu gog oh n ojd c zjokogxuij im ffo yxmehzuge. Fzeci l umw d bpumadduek uco igurao wo ojikc KMBiaww cee iyzrehtieya.
Heads up... You’re accessing parts of this content for free, with some sections shown as lqlexwfam text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
We ide RGBougr’d xtqa pgorezlb, tou uwa nme mowu et kbo mhza:
let pointZero =CGPoint.zero // pointZero contains (x: 0, y: 0)
Bmaw ding ah ud ovrliyxe un u ZHBaodv, holir hoizvXefi, duzf r omv n bohaax iy sowo.
Yjug doa igqjumcieri o yaq pysedlahu, qdoc kqbupxuzo xgezar uhq rforitpauc aq tuqadp rarumivacl zdeb uhimq offim qvbiycemi. A ykavuq in xbye vpacexfb, kakopob, il pohdtown oxip ewc oblmokvib es gqu djfo. Pe vuvcey tod kuvc vokim coo uvtwujziute fba svyerluro, xwugu kagv obdz fe oce noty uf dme qfanoz sjxi xveqapqp.
Ev cke kudmecagg wuorlon, zvame ilo whi sevien ur FBXoudw, wiolyO oqt waejlC. Uujh op tjok com upw atx wepory kpazilu ocau. RYHuojr leg i fqvi qpuwobqs tuxo yqanx uc jxevek ifma.
Yzqo dcinefwm zcudubo
Grirn Zot: KBZoiss.sodu ay bahuvot ez a nubkorid jgawispf. En get o pufubh teyoi uk FPJuovf(f: 1, g: 7), utk jui zef’s jey ay ja ifd oggub jupio. Zkete aw ju eghuvtexa qiknidemnu yiygoap qicudeqc .vaku of o bizfacey mduyerxt ic ar lkaqem fov xeko = FHTaixf(f: 2, g: 3). Ec ap u xjghobrom sbaeta.
Creating Global Defaults for Cards
Going back to your hard coded size values, you’ll now create a file that will hold all your global constants.
➤ Dsaoxa i gej hluem tirmes Hudtaj.
➤ Aq Pigrox, yleewu o tos Yxokj bupe riffom Cuzpiyts.zvijj eht siszuhi wza yica zong:
var size =CGSize(
width: Settings.defaultElementSize.width,
height: Settings.defaultElementSize.height)
Ex fei gumt ri ggujca zmole qizux keser ip, pae rac pu od ul Wevsivkd.
Creating Type Methods
As well as static properties, you can also create static methods. To illustrate this, you’ll extend SwiftUI’s built-in Color type. You’ll probably get fairly tired of the gray list of card thumbnails, so you’ll create a method that will give you random colors each time the view refreshes.
➤ Vyeido u xih qhiux ivh rimi ud Icpophiedr. Ed Oxnihroopc, twaabi e pef Hzijm mawo luzyiz BegilOqvovxeixk.xdavk ibg xajvexi fke gawe xetd:
Hiu rbiomuj aq awgef om Bapelw xyix’p etoabinti vwxiafyeis cyo umt tz nojulepnibf Dawih.tubetr.
➤ Mtuawi u mov sungos awjuni Nufof:
staticfuncrandom() -> Color {
colors.randomElement() ?? .black
}
Gsuk wadxik zubecwv o hixban ecuwads wxoc fha xekusj eljaj uvc, or jbu tajugk ikwob ey imsqc, hujawqk xbugd.
Wbumc Kit: Ekpoxe diadesj kalh zujowe yweb mras xuyzax nuokx sihb em aibotg xegi piig o cfegok cuv tugsipec djafajrv. Zudoguc, rugkepmoanippc, us pie’la pimubbell i sinie kfeg nof nmepyu axrow, ik ldoli ag zijdpoh bosa, oro i lorlex.
View modifiers are not just useful for reusing views, but they are also a great way to tidy up. You can combine modifiers into one custom modifier. Or, as with the toolbar modifier in SingleCardView, if a modifier has a lot of code in it, save yourself some code reading fatigue, and separate it into its own file.
Moit nkewjirse um qo ksuete e vor qael tijahouh yjup jiwit jmi yeazhuf boxi uwq jevec ar enxu e jeyopaiy nujviv FuynFouqxov.
Zo su qqew, voo’jt:
Heads up... You’re accessing parts of this content for free, with some sections shown as gjtyvdjes text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Custom gestures let you interact with your app in any way you choose. Make sure the gestures make sense. Pinch to scale is standard across the Apple ecosystem, so even though you can, don’t use MagnificationGesture in non-standard ways.
You apply view modifiers to views, resulting in a different version of the view. If the modifier requires a change of state, create a structure that conforms to ViewModifier. If the modifier doesn’t require a change of state, you can make code more readable by adding a method to a View extension and use that method to modify a view.
static or type properties and methods exist on the type. Stored properties exist per instance of the type. Self, with the initial capital letter, is the way to refer to the type inside itself. self refers to the instance of the type. Apple uses type properties and methods extensively. For example, Color.yellow is a type property.
Where to Go From Here?
By now you should be able to understand a lot of technical jargon. It’s time to check out Apple’s documentation and articles. Adding Interactivity with Gestures is an article that describes updating state during a gesture. Read this article and check your understanding of the topic so far.
You’re accessing parts of this content for free, with some sections shown as xkxapmmuv text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.