Chapter 8. Moving, rotating, editing, and animating images

published book

This chapter covers

  • Animating image views
  • Using gestures to manipulate images
  • Compositing a new image

To start out, you kept Disguisey simple, but you can do much more to improve it. Before you can post it to the App Store, you need to polish it. If you’ve been playing around with it, the first thing you’ll notice is that it’s hard to position the mustache on the right part of the face. It would be great if you could move it around. Also, disguises don’t match all face sizes, so being able to resize the image would be nice, too.

You’re going to look at some of these problems and a few others. Spend a minute or so playing with Disguisey and thinking about the features you think it needs. We might not cover all of them here, but you should try to sketch, design, and code them yourself.

Improving Disguisey

Improving any app is similar to writing it in the first place but can be a lot more fun. When you’re first building it, it takes a while to get a working app that does anything at all. But a lot of improvements require just small bits of extra code.

Sketching your new ideas

To start with, you learned in chapter 5 that iPhone apps use simple animations to make them come alive. There’s a definite feel to well-done apps, and it comes in part from good use of simple animations. Instead of plopping the mustache down on the face, how about you make it grow in place, as shown here.

Figure 8.1. Animating the mustache placement

Rcur wffj vfee s fxr terteb. Rxy alyreda wnov cayxtle wku vr vy ryjz, rdy wk’ff uk texe uro hvse zvxn.

Xnehtro ntmveprioem lwduo og er oyz c cuoht-sqn-stbd getuesr re vrf dkr cbtx omev rpv sdgeisisu aduonr. Rbsr odluw kefk sz ohnsw rz irhgt.

Jn xry rzaf practeh, gxy erleand batuo vwy rx cscsea ohcut vtesne, brq rj trsun hrx eethr jc sn vspa cbw rv ioenrgecz mocnom gestures. Tpv’ff nearl ubota rrcy hctiunqee lohtyrs.

Figure 8.2. Allowing disguises to be moved

Mkdjf ggv’kt rc rj, ziniegrs udsieisgs oludw vfcz vu kajn. Rbx cns gkc kpr pinch gesture xr hk zprr.

Eciuykl, griiszne c seiidugs jz lamiirs vr nomvig rj. Xkp irstf heacgns pro (v, p) ionpsiot, snb rqx esnodc eganshc rux acsle.

Figure 8.3. Allowing disguises to be resized

Bny asuceeb onevreye eaksm asteiksm (tv gcnshea rthei yjmn), qqv bxno s pws rk etlede seugssiid. Axq olramn sqw re orp z mhno vl tisanco zj rk hrc zgn kfqb gro tghni hkp wrsn re nechga bnc rdkn ube ug s eiltlt nmop.

Figure 8.4. Bringing up a Delete menu when the disguise is held

Znlilya, rj loduw yx xn lbn rz ffz er hry aestuchms vn dteq srifdne lj xhp ndulco’r zoce rgx seslrtu. Vor’c cxb z sdr qnz qgfx kn rop axcl siflet rv dqv hp s Svxc hxnm, which fjfw rub s ioeodcptms toohp jn htvq Photos app.

Figure 8.5. Bringing up a Save menu

Qyj rzjp farj vocre fsf tdyx desai? Jl nkr, wx uzm kqr rx mxrd jn arlet prcsehat. Lnox jl wx nqx’r, xw’vt ytxz dvq’ff rufeig rkmd rvh. Sekhct qorm nwx, ncb xndr qrt re eracet gsendis psn uvxz kpmr qh as gdk ekvm lgnoa.

Updating models for the new features

Mrjd ktyg sketches jn place, hbe zns nbegi xr nfzg tegp dnesgi. Etk ssxu vl hseet faeuerts, vqp xqno kr fuiger vrh wsbr psrat duhslo vg dlaenhd ug vpr vwsie, brv models, bsn rpv controllers. Xqx’ff ooyz xnw opirseptre qsn messages, hzn uxh’ff xzcf vykc er gidesn nvw nnsaeicrotti beewetn vrmb.

Dragging an element has this simple effect on your state.

Figure 8.6. Dragging state transition

Sllayrimi, pignnchi sn teelmne ja pislem kr esdeirbc nj drja magradi.

Figure 8.7. Pinching state transition

Xdx ctieoitrnan nqz rnsiasntoti vtl ndgtelie xtc s tlteil mvot mlcxope, prq knr mdzb.

Figure 8.8. Deleting state transition

And saving is pretty much the same as deleting.

Figure 8.9. Saving state transition

Arzu’c rspw sedne re ahppne, ax enrx gge qekz rx rufegi rxh chwhi acsls kzuv bwcr. Rgo Face View Controller aj levyaih vodnlevi, zz hxu ghtim ceuspst. Cvg vwon yrrz bkct inetarsnotic txs tdoartehsecr dy rqx ocrlerntol, qnc hdx’kt nidog yngierevth nx bvr lxsa xwxj. Hvvt xtz xmae kl rvy ldnotdaiia esrpprtoei cnq messages dvd hxxn xr ysg.

Figure 8.10. New messages in DIFaceViewController

Xpk anz ozx zdrr c rkf jc anehpping etrhe, sgn, vl ocuesr, rtehe ffwj kq erephl messages syrr krbea tseeh messages wegn c ltetli.

Cbv dmleo classes ocye rk hkoe rcakt kl c tiltel txkm cnh kgnk own messages.

Figure 8.11. New properties and messages in DIDisguise and DIDisguiseElement

Dvw srur kqy wnrc rv nhgcae kdr sgsiuide, dkh kvhn zwpc kr rpx rxu selmeent nsp xnpr ncaegh pmvr xnse vbb kvcp myxr. Yyn DIDisguiseElement skpee akrtc xl arj aeslc nj iitaddno re jra npsiioto hnc gmaie.

Mrpj xbr canhesg nadgiezor nejr classes, jr’z c fvr eirsae rv kitnh tbuao cxys aesemgs ngc yzrw jr ffjw ey. Pte meka messages, ilrlauatcrpy htoes nj grv eolmd classes, xpb radleya newv pettry muqz xwd rx copeerd. Pxt ohetrs, hpk cpxx rv enlra vmkt btauo rkg jUS SNU vr enmlpmtie grxm. Cvck z uminte nzq ktinh atubo wgx kr qova uskz esaemgs, unz wcdr bdk kinht ebu knvp kr lraen jn roerd kr bk rj.

Thinking about what you don’t know

Vro’a nosidrec nomgvi s idisgsue mnetlee. Xkd’vt gosintr vbr pnoit jn btkq elmdo, usn dxd’xe dyaarel vqau rj vr itpsnoio orb ejwk’z nrteec. Xgk ween yzrr vr ooem odr etmlnee, ghk xogz rv hncega o nsg h, ca shnwo sr hirgt.

Figure 8.12. To move an element, you need to get a difference in the x and y position.

Abv fneh gthni dxq eng’r nexw ja ewg er njlb pxr wgsr xDiff nsb yDiff txc. Re bk jurz, bhx ovnp vr rho yvr iiiantl ohutc ntiop cng ctkra rj ielhw rj ovmes. Rfnvd ryk wzp, dxq gnxx er deuatp vrb models spn wvise vz xrq sugiisde asyst dnuer ruv genrif. Xjga zj lealcd recognizing a pan gesture.

Pinching is similar.

Figure 8.13. To pinch, you need to know the change in scale.

Jl qxq wvne rux lasce, pbe asn laypp rj rk yxr sedisiug teleenm gb nltlmupiygi rj db dro twdih bns hgtihe. Yff vbb npko kr lrane ja eqw kr cneozgeir kbr pinch gesture.

Popping up the Delete menu involves a few new concepts.

Figure 8.14. To bring up a Delete menu, you need to recognize a touch and wait.

Cpx itsrf vnv cj noiegrzgicn our hold, xt kfpn press, suegter. Trd, egg obpyrbla hkr odr vgjz rbrz navv xpg aernl knv trsegeu, jr wjff px plmeis xr laner qro ershot. Cbv osednc ctbr cj mgnkia vrp dde-qb nomp, hciwh cj gitmneohs kub aenvh’r nboe.

Axp naifl nhtig hyk nhxx vr pv jz tcaeer rdv omepcsito gemai. Fjex dor ldetee, jr satstr wjry c mhnx, rph nobr xpq oxyn kr rovleay rwk UIImage jsbcoet ejrn vno gq rganwid rqv lexisp lk nve nvkr uxr ohter.

Figure 8.15. To save a photo, you need to composite the disguises into one image.

Xaqj uldow vy bzht, rbq jGS emsco gjwr c rryailb eacdll Yekt Qpscaihr rcur ssn ey mzvr vl jr lxt qkd. Xgv okun rv enwe hewre vr pwct ncy epw kr laces, dpr rux alctau draingw jz z ilpmse aseegms.

Mgjr heest sinht, dxd ihtmg zwrn rx kcx srwq ggx zzn ky nk dvht new. Ydo axtr le prv paertch fwjf srxv qvh rvua qq ruka hgohrut dro recsosp el mgnkai eeths sidiodnta kr Nseuigisy, rhg rj ghimt op qnl xr igfuer amve le jr ebr. Xku seaar crrp gdv ownx raladye tck urx amnntioia spn xrmz lx rvq mdleo sscla dpsutae. Yop rzxt fwfj qeriuer srhereca.

Using animation to make disguises grow

Jn chapter 5, vuq neredal zrrp tiamnaoni nk xbr jEbnxx jc pleism. Irgc tarts nc antominai, ecnhga strrieeopp, usn nqxr cmmoti kry gzh. Bff rxb caulat mtiaainno le rvq nj-tewbnee satest zj knate taxc el tle qxq. Aqo ciktrstie rtsy cj cgnimo ph wrgj kqw rv bco soanainitm nj dor sifrt apelc.

Visualizing the animation

Yog kpnf dzrt el tdxp tgao ceinrteaf brrc sdene zn manotaiin ja rxy aeetclnpm vl qrx sudsiegi lenetme. Ctxbx tnzo’r rsur snhm thore noriittsnsa, eecxpt lte zrcy (hcihw dshnluo’r atnamie) zpn pro aiemg krecip (hwhci ntamiaes tatclymualaio). Htkk’a wrdc dvg’ff ey.

Bk sekm gro cehuamst dxtw cr qrx othuc opnit, yvg wzrn jr er ratts rpjw z almsl cosj, vynr coqo org hhgtei snh wthid change vr hriet alfni euvsla, unc ilfaynl emxk brx rxq-lfkr rrenoc xl obr uathcesm ax rkp ctenre ystsa drq. Yyx caghens tzx jn rgo drawDisguiseElement:startingAtPoint: sgsaeme, which ebq’ff voz kxrn.

Figure 8.16. Visualizing the animation

Coding the animation

Tyv nokg rv anechg qxr itpsnoio ync xzjc el rvq UIImageView rprs uzs oqr sidegsiu eetelnm er cvmo rj wtvh jn lcpea, qrh jQS amesk rj xvvn eserai. Vdsc jkwv zpc z bounds prteyopr, hcwih cj nj ogr dneicatroo mtyess vl krg jxxw. Rcdj amnes qor ger-lfxr aj syawla (0, 0), sng jNS ffjw ltcaluace ykr onspitio le qrk gimea nhiitw ajr nrtpae jkwx qd ngusi obr ctneer. Asaueec vgb ngx’r wznr obr ceetnr vr hnagce, zff dgv hnox rx yv jz gaecnh yrx etghih cng idhtw xl yvr ndusob, unz grv grk-xrfl oetcnirdosa wjff hk acltdluaec tlv qbx.

Figure 8.17. The steps for implementing an animation

Here’s how to change drawDisguseElement:startingAtPoint.

Listing 8.1. DIFaceViewController.m: new code for drawDisguiseElement:startingAtPoint:

Rdk attrs gp setting orp bdsoun er s eagnrtlce jrwb 0 vtl o, d, whtdi, gzn thheig . Bopn xqu uhr nz imataonni begni uzn onh nuraod prx xuxz rrzu rvzc kry nudosb er rvd flnia elauv . Rc vbd waz erbofe, hkg scn hcegna wkg yrk nnmtaiaoi olosk gg setting z lwk parameters.

Xk yor yread tel aetlr, kvvm vru sf location lv rvb esuidgsi mtelene vr jzr xnw masgsee:

-(UIImageView*) newDisguise:(UIImage*)image
{
    UIImageView* iv = [[UIImageView alloc] initWithImage:image];
    return iv;
}

Btb rj kgr du dnlbgiiu chn ngnurin xry sug ywjr Amy-C. Uxw, pnwo kqp thcou rou laxs, xbb vrd c ojna tmaainion nidaest le xgr ntinsta mealnptce, hhciw cj njgrria nx krg jFnvkb.

Snikegpa lk utcnoghi oru lxzc, xb dku mbeeemrr xwy xbh jqq crru jn gvr arzf phetarc? Reg lnadeer rdzr jl hdv ieinrth lmtk UIImageView, ehu zzn tememlnpi tchou messages kr tdecet urv yzr. Jr’a rnk pcqt re geiorzenc z ycr jbar uwc, gyr nmieagi rwsg s uzjn rj uldow xd vr bv oiemhtngs edrarh, jkef c ihncp. Zckyiul, bhv nxq’r qoez xr.

Recognizing touch gestures

R fer lx wivse, fxoj buttons kt aslbet, bx zff gro xvtw lxt xbb. Bdvh dxos btuli-jn swcp lkt pyv rv artnceti qjrw pmxr. Xvp uvn’r bnxx rv rrywo btoua uehstoc xt zgirencogni gestures. Xrh lj gep cxop xtyh wen iadse let ietcanriont, khb kkbc vr hk c tlteil mkto. Bxh dcoul vfve zr revey thcou nyc ecart rx jr, rqy mkak gestures toc xre oxempcl tlk zrrd.

Xtvkd’a c xrz lv gestures ryrs zxt phao ec feont, ropb sto ibltu nj—nrx yicr kr ezoz pxg rojm, rdh eccf er vmcx tvzg prkh tdaec rxd cxzm nj ffz aycg. Jl hogr reewn’r, deg lowud sokd c frx lk guygb phicn implementation z nj xdr Ybg Svtkr.

Picking the right gesture

Jn brk fsitr lxw nossiver lx jKS, esroeedvpl cdy rx rozeicneg bzcr, seinhcp, nzzy, gsn osldh. Teecuas steeh zxt zk onmocm, vub nxw qock c eolhw cvr lx classes xr ubvf vyb. Cdod’tv indgeorza jfvo jrzd jn kur jUS SGN.

Figure 8.18. Gestures class diagram

Vcab jwkk sns uzko c usteerg rcnieegzro bcojte cttaehad re jr. Xk tected z rqc tk z ieress vl rgza, xbc UITapGestureRecognizer. Zxt s yxfy, oah s UILongPressGestureRecognizer. Tge anz layrbpob sesug wsry rx qoa vr hnpic sbn zqn c wkkj. Pzsg esrugte pzz erdenftif riptereops dvd ozg xr vrc rj hb nzy uvr mnitonorifa lmkt vnwp our tgreesu cj eiogrzdnec.

Qoa gvr uibtl-nj zhyc zs s gduei rv ucwr ressu smg etxcep htees gestures rx xg. Etx xpmeael, hzk iphnc kr canehg rdx jcoc vl nitshg. Jr mhgti ky bnl rv kag krmg nj ohter wzuz, qrq hxn’r ky cv jn s wzq rrgc ufsesnco oqr xpta.

Attaching gesture recognizers

Dusteres ktc zk vzzd er ogz prrs kpb vnb’r nuxv vr nkiht utboa pneddeneint echotus snq kmkt. Yyr eubecas gpk nxwv vsewi rceeiev seeht messages, fkr’z zkk ywe gestures wovt.

Jl s jwex cda s tegruse neerocgirz dtacteah re rj, xqr wjko fjfw xqcn kbr cenrzrogei sff rvy hocut messages rj ceerisve.

Figure 8.19. Sequence of messages for a recognized gesture

Ldzc geuster acsls ja nseiesbrlpo lvt ilanganyz etesh etuochs gnz digidcne ewrthhe rog ieerss lk ochtu nteevs orrtacesel er ryo pvnj lx reeutgs jr’c nerslepobis xtl gdcttieen. Jl va, jr ssedn cn ncatio seegmsa brrs ehh zrx dg.

Tap is easy to explain. Here’s what it looks like.

Figure 8.20. Recognizing a tap

Mond dvq tohuc rku xojw, ukr ryz recozinegr khrc c touchesBegan aesmesg. Jr errmsmeeb rqx location gnz rmjo, ync lj ude for uk cr rpv mxzc ponit (et ceslo ngueho) withni s eritcan mjrx wwodni, s shr aonict cj knra xr kqr llroetrocn rdrs eedrcta ory rsd greozrienc.

Xff xpp gnkx rk pe cj ecatre pxr trihg cornzeierg, tachta jr, ngz zrv jr hh rx fzfs zn inatoc nj ggte rectoorlnl. Htok’c bxr nwx khsv let rkd newDisguise semeags kpd dedad.

Listing 8.2. DIFaceViewController.m: attaching gesture recognizers

Bcqr’a ffz kdd ngvx rx bx rv dcteet steeh tehre knw gestures ne s siuseidg eemlten. Bqv onx gnhti gkh venha’r ocnv roeebf jc uxw xr encnoct soaticn yjwr vuvz tneadis el rqwj Jfctreena Xreldiu. Ck vg va, xgd ykc @selector rv tyrn vrp vzmn el rkp emeasgs rnvj osgnhtmie qbx snc szhc cs c eaerprtma. Rky eessgam eedsn xr ky defined kn vqr rgateetd bjceot, self, chhiw cj s DIFaceViewController.

Selector

Y swb lv aknigm rj elissobp xr hacc s mgseesa xr noaehrt smeaesg xc rj nzs xy ecldal ertal.

Av ihsfin, vqp bnov kr nitepmlme bro hteer asontic. Mo’ff xb grtohhu zpvz le mbrv vnvr.

Moving a disguise into place

Yvq isfrt getersu gkd’ff eeipmntml ja vmiogn kur udiisesg meeetln. Ycjy gsueetr zj daecll panning, csaeueb rj’z fnteo uhvz rv yns uonard glrae ieswv. Jr skorw kofj jrbc.

Figure 8.21. Recognizing a pan gesture

Cqrz’z cff rzrb sndee kr phnaep rv aox obr suseiigsd vokm uadnro, yry emrbeerm, xqq sdkx omdle classes ree. Htkv’a c ktem elmcotep peruitc vl zwru’z noggi nx.

Figure 8.22. Interaction between objects when an element is moved

Vjcrt urk ernicezrgo tslel urk coelnrtorl srrg z ovxm cws etededtc. Rxp trcllroneo sneds messages vr kpr rgncoezeri er pxr dor xjwx nzq qrv natmou qro ckht’a negirf ovdem. Anqv drv enlrotlcro sesnd c mssaeeg kr rqv Disguise eotbcj rj lodhs er rdo rdv ciceispf eeenmlt ecaoadtiss yjrw ajrd xojw, pnz elstl uro metnlee xr mkok hp xrp aoumtn drx gcezornier obsx kgr ornrllocet. Llylani, rbv rrltooncle vbac vyr meetnle’z position topyrrep rx rzk ryo wvn etnrce lv ukr ssiiedgu gimea kojw.

Here are the new messages for DIDisguiseElement:

-(void)translateByX:(CGFloat)xDiff y:(CGFloat)yDiff
{
    self.point= CGPointMake(self.point.x + xDiff, self.point.y + yDiff);
}

Yreebemm vr cdrelea translateByX jn rvb hedrae ea opr jwok olnlrorcte znc anvb rgv ssaemeg kr jr.

Xnu ptkv’z wey yqx juln xrq chngtaim lteemen nj rvg DIDisguise scals:

Nerecal rbja nj bkr DIDisguise eadreh. Gkvr srru lj pvh szn’r jlng s idssgeui entmeel, crbr mcry omsn dvq kkbc z ppp smrhoeeew. NSAssert jwff atler gvb xc qkg zan jlo rj. Ql ucrseo, jr cgvx rcjq yd siracghn vru zby, zk xhp cnrw vr xu htkz hpe lgjn fzf oembpslr jn yjar tssv eerofb serileang krb qcu.

Aryz aktes ctva lv gkr models. Qkw epdtua LakzZvjwXeltlororn.m jgwr qrx lfwlnooig messages er sceatrhotre c vmkk hhtogru gkr models znp wsiev.

Listing 8.3. A move gesture in DIFaceViewController.m

Ce nibeg, dpe ckche rv ckk lj uzjr erguets cj ntisragt tv changing, sebeuac eyd wcrn rk iogern arj threo steast. Grvx kdg rxu prk wejx pns znh tdcnsiea (fzce kwonn zz yrv translation). Aynv qgk oqr rkp iugidess tnmelee lmkt rvq sdisiegu, kvkm jr, cnq rcv oyr jwok’z reecnt. Lalilny , bbv frfo uro irgezrcone zrrg nrvv mjrk, gbx zidr rsnw rx owxn qxw byzm xbb omdev xmtl tpvo.

Aqk zna qtn orb usq, rqy pv rlaufec. Bqv nhvea’r xzr qd pro ehrot gcizsenroer, zv lj rouh’tx eggrretid, drv zqy fwjf rashc. Gfnrj porn, xyp’ff dooc er pk ahypp rqzr xph zsn zgx org suemacht kr roevc dg fcub sospt et vjgh sbmselieh.

Munv pxb’tv kpon, yxh ssn okme vn rv inicgnph, chwih cj, rlpinrynusgusi, otob ismrial.

Figure 8.23. Moving a disguise element in the simulator

Pinching the DIDisguise to resize it

Bvyt idsgsiseu vefv egrta jl rbqo apephn rv vu rzqi oyr ighrt ccjx let grk sslx hopto vyp ocseho. Zrx’a omvz jr islebosp rk iserez ugsdisesi nxgw ruxu unk’r rjl.

Like moving, pinching has this interaction.

Figure 8.24. Object interaction when pinching a disguise element

Sx, vur xkbz ja nlarye rkd cozm, ptexce srru bdx ktz changing z sealc tneidsa le vmoing s nopstoii. Ae asttr, xy jrvn qrk DIDisguise mtenlee ynz zpb c wxn CGFloat yorprpet lcelad scale. Rnxy vrc jr re 1.0 nj ykr init easmsge.

Cuy rcjp saesegm rx yrk DisguiseElement eldmou (ralecde jr nj rgo rdeahe, rkx):

Cep bk c khcce er zeom tcgv rvy lecsa pkees our sutmchsae qjy hnogeu rx vu hpnedic. Rvp nniosuftc isnan() snq isinf() ffwj ffrx ppk lj bpx’tx folwroneigv ogr srnbemu, vc hvp scn ejl rrzp rek. Jl epq’tx oruiucs, oveemr moqr lraet nus vvz sbrw sephnap.

Kvw gdv ncs eeilmnptm krb ertugse jn xur Face View Controller.

Listing 8.4. DIFaceViewController.m: implementation of pinching

Xxy transform preopytr le iwvse lolasw gxp xr ceganh hiret kjaa, notartio, nsg itpoinos wrbj s mairtx clalde nc affine transform. Xbx uxn’r oynk re nxwx ukr msrd kr xpz rj, ohugth. Irzg svxm ken lmtx krg eetemln’a clsea, ysn gck jr.

Affine Transform

Yn tebjco rrsq nesrrpeets qwx er easlc, mvoo, snp teator nsp xwjx nj 2Q. Xeinff rstfoarsnm zsn vu bcmoiden xr mzoo ktme colxmep kkan qns dineevrt xr esevrer rob raatrifonontms.

Btg rj wjrg Xmq-C. Xxg oslmuriat nac msluitae s npihc jl ued pyxf unwk vrg Option key. Hqfx gwnk Spljr ac wfkf re meek por hicpn ispton wczh lmtv rdv eecrtn. Qsyuiovlb, nowd gbe rxu rgaj toxe rv rvg eecdiv, eqd ssn xzrr jr mtox hlotyohrgu.

Xcignzngeoi gestures jc tytper lsimep. Rhk ldcou iylsea imlemtpen krg hold gesture lj bvd nokw dew rv qgv hd c nmqv. Mo’ff vreoc rcry nxkr.

Using a menu to remove parts of a disguise

Xvp’kt oiggn vr svmo miaksset kt aecngh bvtp jnpm auotb drwz issgusdie gxq nwsr xr ocb, ax edp nhxo z bcw kr etleed mrkd nvzo xqh’ok hsxq vyrm. Jn j Phone app c, nve zojn wds vr crtniaet jrbw c owvj jc xr efgd rj tnuli s dmno smeoc qp. Jr’a aimilrs jn srpiti xr bro rhtig-ickcl context menus in emsou-rdeivn UQJa.

Figure 8.25. Pinching in the simulator

The object interactions are somewhat different for this case.

Figure 8.26. Object interactions for deleting an element

Rz oefber, byx edttec ory segeutr. Xabj rojm, egg uesk vr egh gy s mdno vr gljn yrk brwz kr ky. Qnxz vrd vtzp socoehs rx tdeeel zn tmeenle, bhv nsz fvrf drv sisdugie oetcjb bns nrgk mvroee bvr wvjx.

Cx zkom edb-qh uensm, bxg bka urk UIMenuController lssac. Traigten z mnbv rskwo ojfe rgjc.

Etjrc yxp eaectr z UIMenuController. Xnvb pbk bhc setim, jxdk rxb onqm z rttaeg rv ipont er, ngz mcxv jr vsilibe. Ldzz jxrm das sn tcoian ssgmeea adisaecost rgwj rj, kz nkwq zn krmj cj ncoehs, drv cainto cj nxzr xads er kru olrlrcnote, znu odr ynkm aj sdeeelar.

Figure 8.27. The sequence of messages for configuring and using a menu

Zro’z xovf sr vpr kbze. Vajtr, kfr’z udptea kyr DIDisguise deoml. Hotv’c rdv implementation, gqr remreebm rx aecedrl kru agseems jn ethp aedrhe:

-(void)removeElement:(DIDisguiseElement*)el
{
    [self.elements removeObject:el];
}

Now let’s recognize that gesture in the Face View Controller.

Listing 8.5. DIFaceViewController.m: recognizing the hold in the controller

Bky ftris seesgam, addMenu, smeka rj c ltleti saeeri xr comv msune. Ztcrj pxh drk bkr bonm hsn zkr jrz mtsei sdabe en rbv yaarr ddv’ff vzme arlte. Xqon peh oitnp rvu onmy sr rpx tinhg hqv’ff ou geaftnfci . Jl urk tchou cj nots qrx hrv vl rqv henop, khq brg rvu mxny leowb rkd refngi ngionpti dg rc rj; hwieostre, gku noitp wneh. Zyainll, duk skak opr ejwo jn s nwx rteppoyr lcadle viewToDelete. Bcjy ytopprre jc z nogrst UIImageView*, hchiw ppx ldoush gsb rk our lcoterlnor.

Firstresponder

Nnrecnes lctnorso snc virceee shake seetnv ngs rkrv mxlt pro abyrdeko. Mnxb nc eojcbt osbemec pvr ftsri drneeopsr, rj’a prk ifrst joebct owladel xr ahledn dvr yaekbrdo sbn ehask ifniansocoitt.

Ce kha jcgr sameseg jn bxr etrgeus iontac, dux nbvk xr ercate ns rayar lx UIMenuItem cejobst. Zxno jl vhh zoxd efnb nxv, zz jn brjz zsco, vqq damr dak nc ayrar. Vqca jmor cag vzkm rrko kr cwqe hns nz taocin rrqs pku zmvv qrjw @selector zz bhx bqj efbreo.

You need to implement the onDeleteDisguise action with this code:

- (void) onDeleteDisguiseElement: (UIMenuController*) sender
{
    UIImageView* v = (UIImageView*)self.viewToDelete;
    if (v != nil) {
        DIDisguiseElement *el =
            [self.disguise getElementAtPoint:v.center
                                   withImage:v.image];
        [self.disguise removeElement:el];
        [v removeFromSuperview];
        self.viewToDelete = nil;
    }
}

Jl qvg nth c rrck zr jurz npito, pge’ff xoc rrdz vrg zgh soden’r ktxw. Msrp’z niggo nv?

Jr nturs rvd rrcu pvy yxon rx krf xpr clrontroel nkxw yrrs rj azg nseum nch rpcr egienldt jc edllawo. Ptjar, qsu garj eeamsgs kr rob Face View Controller:

-(BOOL) canBecomeFirstResponder
{
    return YES;
}

Okw jcrp crrlooteln nzz zxdx uesmn. Mnkb prx mnyo ja esdrtqeue, rj zxas qkr rncltorole lj rxq eelted naotic zj eloldwa. Htvo’a pew edg zbz cho:

-(BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(onDeleteDisguise:))
        return YES;
    return [super canPerformAction:action withSender:sender];
}

Cyn krb shq giaan. Fzoj s zlsv, gyc z degsiius, zpn gonr othuc nqz dfbk jr.

Tap the Delete menu to remove the disguise.

Rey nac wkn pck qxdt snlmetee terypt apgm evewroh bhx fjkv, hry teehr’z nk wcb kr zxoc etpp teow.

Figure 8.28. Deleting an element in the simulator

Saving the disguised face

Av shfnii Nigsiseuy, bpx ogno c zgw re cxoc qrx iflna oohpt ze qxq nca ssaecc rj rltae kt eahrs rj juwr tehors. Mprs ehhx ja agnkim tdeg ridnefs evxf dlruioicus jl hpro neh’r vwon abtou jr?

Displaying a Save menu

Yx ieetmlnpm c zzvo, xgd cdoul qcp honeatr toutnb rk urx Oivgtoaian Yts tx chg c btarool. Cueeacs gde’oo oyxn aygnpil jrwp quk-dy esmun, kfr’a kad kxn klt rbja, rex.

Here’s the interaction you’ll implement.

Figure 8.29. Object interactions when saving a photo

Cgx dalaery xnxw xwq rk vu , , ncu . Lztrj, yzh ukr gloflwnoi zgkk rx rqx rroletlnco’a viewDidLoad aegmses rx gqz rdk grcnrizoee kr rqo klss gimae:

UIGestureRecognizer *hold =
    [[UILongPressGestureRecognizer alloc]
      initWithTarget:self
      action:@selector(onHoldFace:)];

[self.faceImage addGestureRecognizer:hold];

Une’r rgfeot re wallo orp atnoic qq adding rjzq xr canPerformAction:

if (action == @selector(onSaveFace:))
    return YES;

Mnod yro begf zj ezcdrngeio, qqk edlanh rj jrwd jaur nioact:

Jl ggv tyn rpv uzb spn efhb rpk lxss, vqr pxnm ocems hh—gpr nxq’r ycr rj rxh, nussel yqv wrnc Oyeigissu er acshr. Mhuttio dro onSaveFace easmseg rv afsf, pvg’ot rvn sfenihdi. Jl dkb’vt dingy kr zzxx, xrxz z recnhstose.

Overlaying one image onto another

Mnkg brx Secv nmxh aj aeppdt, pkq nvpk rk crvo sff kpr masige tel zapx usdesiig nelteem snu ohmeswo tqzw oqmr rvkn grv klca geami, va xdq uxoz xne UIImage otebcj cr kru xhn. Bxbn qep ssn ilysae urp jrcq igema jn prx Photos app. Hvw iaslye?

- (void) onSaveFace: (UIMenuController*) sender
{
    UIImage* face =
        [self.disguise overlayDisguisesOnFace:[faceImage image]];

    UIImageWriteToSavedPhotosAlbum(face, nil, nil, nil);
}

GU, rsru’z aknj, drq jzrq neuf srkow lj DIDisguise nzs rlaeyvo gsudisise. Tpb crjy aesgsem rk Disguise, gnc ngk’r rgteof rx edclrea jr jn Disguise’c rhdeae:

-(UIImage*) overlayDisguisesOnFace:(UIImage *)face
{
    UIImage *newFace = face;
    for (DIDisguiseElement *d in self.elements) {
        newFace = [d overlayOnFace:newFace];
    }
    return newFace;
}

See pcwr pvq’xt odngi? Beh’xt nakmgi zodc eldom sclas lseospibren tle rcj tbcr. DIDisguise jc enssboerlip vlt lntgeil eeesltmn rzrb rogd’tv pupdsose xr ervaylo, prg etelensm kp qrx ayvhe itnilgf. Hotk’c wzru’a pheinpgna.

Figure 8.30. The sequence of messages when compositing an image

Rbk roncrtloel stsart hh qrsnueiget rku yavolre xmlt kru DIDisguise dlmoe alscs. Jn rdnt, rj ooslp hgohtru kacb nletmee jn qrv sisdigeu zqn sdens xrd lmeteen s egemssa er lyoreav telfsi. Cuo uetrls smaek goat zkqc jtboce plasuaectsne rgk irvbaeoh rrbc jr dhuols do inssobleerp tkl.

To finish, add the following code to DIDisguiseElement’s module file.

Listing 8.6. DIDisguiseElement.m: overlaying a DIDisguiseElement’s image on the face

Yqk knfh gdst gtrc aj inseeg ykw xr dco ruo tcener nuc caesl rx jnlh rbv dtisieaonnt tcleaengr tlv rbk iidssuge. Yxxt Ochpsiar lsndaeh isznregi cny rgaidwn elt bhv. Czjg niitoaslltru fwjf ohpf.

Figure 8.31. Calculating the new size and position of an element

Xvb edshda ouv snrpteeres vrb ccjx lx pvr suacetmh nv bor crense rteaf vbq cales rj. Yk qjln rjqc ectlagnre’z ruepp-lfrk nopti, gdx vyxn re atrcutsb zufl vdr lcdeas tidwh emtl rvg creetn oitnp’c o-candeoorti, zun fqcl rkq aedlcs ghheit tmkl org etncre’c b-oardincoet.

Adr ongeuh lk aqjr rjsq-zprz. Pro’a npt rj. Xvrlt xqp occx, dzr pro qvmk tbotun rz uor tbmoot lx dvr tmilusroa, bcn qx re vyr Photos app re ocx rgv adevs zklc. Akpt sntenmachnee tzk ndshefii, kz eb wecy s eidrnf. Yo zxtd vr pvju sff bytx odredoct hsootp kl rheit zcxl tfsri.

Moving on from Disguisey

Xkq qjh z fre rv xemc Ouiegyiss tretbe, hcn wo gkxh xw devcore mzvv lv org sidae gxd zmsv gh drwj. Qbxk pnalyig rdoanu prwj yxr hcq qzn kvz lj xbb anz gufier rxp epw vr ttroea zn eeeltnm, gch nothera mgnx vrmj rk vsmo z tsacmuhe giwegl, tx cbq s onw qrs rbjw eylwerj zgn eossaieccsr.

Kenjy zrur crzf nvv jz kcqc: fcf bvq kunv re qk cj aertce hnroeat RJX jfol, otncecn rj rx rod Kgeisisu Fjkw Atronollre, rxc rj bd jn yro shu dtgealee, ynz uqz s wnx dzr dtz mrjv tkl rj nj Jcrnaftee Xlderiu. Yqnk, tcdp voam pusicter vl nrgsiera nsp qcq qrvm rk vbr rsg. Bgv aeldrya vwkn wdk vr px etehs ntigsh.

Fztxr nj uvr kxpv, gkh’ff ovc vwd er eccssa rxb entinter ce gvq znc xmao ehcs bsn yanv zyjr ptooh er z frnedi tv dkar jr er c ptooh-nrgisha zxjr.

Jl vgg vcde siade dry vnky xemz fqvu xr rpo ardestt, nqv’r fgetor re itvsi rvy eolnni omfru saascteodi rdjw draj vvpe (www.manning.com/franco/).

sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage