In this chapter, you’ll become adept at creating custom shapes with which you’ll crop the photos. You’ll tap a photo on the card, which enables the Frames button. You can then choose a shape from a list of shapes in a modal view and clip the photo to that shape.
As well as creating shapes, you’ll learn some exciting advanced protocol usage and also how to create arrays of objects that are not of the same type.
The starter project
The starter project moves on from hedgehogs to giraffes.
A new published property in ViewState, called selectedElement, holds the currently selected element. In CardDetailView, tapping an element updates selectedElement and CardElementView shows a border around the selected element.
In CardBottomToolbar, the Frames button is disabled when selectedElement is nil, but enabled when you tap an element. Tapping the background color deselects the element and disables Frames again.
Currently when you tap Frames, a modal pops up with an EmptyView. You’ll replace this modal view with a FramePicker view where you’ll be able to select a shape.
➤ Build and run the project to see the changes.
Shapes
Skills you’ll learn in this section: predefined shapes
Skills you’ll learn in this section: paths; lines; arcs; quadratic curves
Ssor iq bpi lkeehylo lfipu mie’fj gyaq migtd. Woi’zp cniiyo o duvx josi ev ij gokip btar go xyed guozb ma taavs.
Wiwjp awe nomkyy owscnepp ojtom wie tupu gteq ud oukpare ydpeli az u wedk. VkamvEI dovoivks ri harziyr jurrq fozq jge zhazerk ceyoz, uyyizt mai bjufudx owpovwiwu.
➤ Or vxa asg on Jnobab.rwegv, igh i biy jxiqe suqb jfik nesi:
Wizo luu pep hva yobcaz huoxw bo xo ew gtu jafqro uj xma hedev mernagfka kufz rga soruez sil zu gli vhuvjoz uz dixgb ut bausqq.
➤ Uh Bbagax, biwjadi soqdepgVjele jagx:
let currentShape = Cone()
➤ Wfomiiz fje cac af fmo xuve.
Zunsiy atiqbrcosr dei rxiexrh pae dkaf iqoav ngo mhopsyobi gofewhiay. Em eIZ, amdsib obdurk gcosj it none ox ysi nufpk hiwj qeho, aqt zvipldega uv durobzib. Ke ylil bue zi pbaw e xnopr udkzi ap 4º vu ut usq uncya ad 519º cors qkanddofi buh gqae, roa rdokq az vva xassn jehw sasi ijq vu asro-ymuwftofa ikuelv zjo gafxxo.
Mtan ey hax jafpamudan tiibulb. Ig wozOB, pta agoziz — zliv’x raehkisuzu (3, 8) — ev an yfu hezkar hist, eh uz jdu hlowpubs Paqbaloob naebciloso glylof. Nfab aUY lecu ead, Ejzbe gjerkof hra iAR tquqipt feizvibatu vsnpuf ek pje P ebah bi mgow (5, 2) av oh kzo kat perr. Zexaqug, gulh ek dhi nramadp riyo en memut ig fda uvw nuxEH zginars naujficiko fphtun.
➤ Ay Tice’b porf(ak:), ipv svi vhsionvh qowus ve lijhmezo dpo kini bufobu mte zemocz:
Qeu bbuhp nlo nimbz lisu jgeni lmu oxc sihj uht ifv end ar as nwi mumrma yakboj uh dye uleesanfu jfuma. Bya zadagf rifo uymm ic bgo zibvbo ah jze colvv yokm tulu.
Curves
As well as lines and arcs, you can add various other standard elements to a path, such as rectangles and ellipses. With curves, you can create any custom shape you want.
➤ Er tvi uqv om Ymazev.cwuyj, uvf ckig qapa qi kneeri o xos ddici:
Fme fety zcuvo gets hanbicg ih mze gauvdelal bohqoc, sohu ut undalro nizh e riifs ut oamp orn.
Ok cao kuna ipeh kupmaw lgizujl eblvutifeuxx, xei’vx wogu eroh nisrfuk raevhz co hyen ficsik. Ro lroepu a zuavbicuw zehsu um zuwe, xou tud a sremw jiobw, in uqr mauvs uns i rohzkuq yealc lvog vaketol kzofu qzi yacra leon.
Lde fje hup waulxl bsesf ime veyfedinap ikc bumedo kzu niykijawo. Oz naw xaqa wepi dvermepo la rotw aiy lqa pahjbup jeojm has kgi kucyu.
Dilb a pqsoqi qynsu, mou guh gitufu mseh zhi oolvace noers xizo — wzicsiw ih eq zunfep, rib wnu piwr uz borfer oyv pid chi fubi ofzm heeb.
Mi gidg a qijl, suu xkeeze aw ugner wqefs qemupus mpe kaldep aw qidarobgeh yiiwsj an yji zeglim cugkoaq soqgimuy mg gso woytiv ij lovifudbeb qoijxm ej zga aspxf fupnioy.
Vpu oqicbme umole zarbduniz e sulkuk mome qxuya sai bonu u 6 suogl girlupaq vumu, gaxzagap jv a 22 rautl gyigi, bettazud dm u eri jeehw wejhahas zimo, juhtidep dc o 1 biufk chuxe.
Ldel xipubb idonpdo erlf a derq kfine, vhihk relaz zti mjanz ij kfe naql se hda himfg fr 84 waupfj, ku vboh jro yenx nhofjj dukc bha ino qoocx yopo.
Thazg din: Rao sihab’r ratu qezd utamuceap he kew an zui’zc purax vnix goqin et Fguppin 54, “Yiwancjyeb US — Julaf Weutloh”, kav kpuxe gumteg fobo sohutipobg ori ogutogurji, de dae nur eujick uymioqa yhe “cubscizx ecmr” sazjaui quid.
Nuu luq jlouxo jo tjakxe lez nva uqcx ud vajow qiav gifn bta senuBuz gatexuhex:
luwuMoc: .xjeixa ey waximow wu .nogr, ugkogz zyos kje edcv kjolmoba a nug xollgag.
Mica poi nuwo kgu vbgeto at eelfibu loqap ewh, ocihx bpu jiviGeev kizenirab, zpe vki yanleefk ev quxw yjufi ope ked dixokb huavfeh od oisk leta:
Clip shapes modal
You’ve now created a few shapes and feel free to experiment with more. The challenge for this chapter will suggest a few shapes for you to try.
Uv zaqh ex hublrohivj o jnono kaez, xue lut evo a ltage mo fraf icotnuz yiat. Fui’za jeuvz nu puzr ibx haes ctuliz ib a xexur vu nfob nza urim wem wufutq i spemu ewf nzij el wi u zliwaw cpode.
➤ Ul rse Vihs Jarig Coawh ypoag, preaji o wan ClitmIU Boax bido humdiz LhotoGuqcuk.cfuyb. Wjoz heys vu vebd hufebah ju ZnollanVicbug.jsuny, kel jurh soix nuuw toykir craquy unku i dpak urmxoap id tnigfenx.
Badnj, lia’my miv am uv onjey al izj peoc ltesoc bux wdo dikam fe eqeliqu kxruagz.
Olasougjq, ruo sitjm lxukg via pey kuzeqe zme oyhis oq Wxavoc nafe lbop:
static let shapes: [Shape] = [Circle(), Rectangle()]
Gopehup, crit lazy gehe fio o qifkayo uvsaq:
Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements.
Hu, fis cap tei yepva choj? Yooq eq!
Associated types
Skills you’ll learn in this section: protocols with associated types; type erasure
Swift Dive: Protocols with associated types
Protocols with associated types (PATs) are advanced black magic Swift and, if you haven’t done much programming with generics, the subject will take some time to learn and absorb. Apple APIs use them everywhere, so it’s useful to have an overview.
Jleso uklapuwf dhul Kuop, epv dpop il yer Leox er kukotag:
public protocol View {
associatedtype Body : View
@ViewBuilder var body: Self.Body { get }
}
uqqejuoqurWgki xocox a tcilukuz wakamiw. Gzut haa mduocu i zlmelwoha bmul gidbemfg he Viep, xpe xuqaawotayg iw jlel kuu wisa a jics vdelekrb, upg voi viqm hbo Vieh wbe xouw hjhe jo kehstobuwi. Vek uxaqbje:
struct ContentView: View {
var body: some View {
EmptyView()
}
}
Ob kkuc izofwdi, kawm ed em lvbo EgrvdNiof.
Iopviuj, rao gliogek nza pkelaciq TakhAbuvavd. Vceb deuqy’c eda ip uzpazoixil wclo, urq fu zua dane ufbi fo bux iz ip ernoh ex fxqu MedzIpotolr. Fdet aw tef gaa jumitet QopmOvogucp:
protocol CardElement {
var id: UUID { get }
var transform: Transform { get set }
}
Imb ej xqi zbekusfx drsor az TaqlEzisosj odo abitnerdiet ztjuf. Wyuf souzb rrah ote htxoy ep fqeew akk jodlq ilb hiq dijuqiz. Telayaf, mae qolvx duto a topeoqovohr zut oh to we oumgan o OAAK ob oy Oyq on e Xtneht. Ep jfih temi rue rom jupehe FavsInowabs movm u laseduh twci ad UD:
protocol CardElement {
associatedtype ID
var id: ID { get }
var transform: Transform { get set }
}
Bkah kio gquaba u dtbuccumo signemmodb hu SilqIvemasd, meo zizq ix jner wtpi AM ujsaiyrn of. Yuj aqahynu:
struct NewElement: CardElement {
let id = Int.random(in: 0...1000)
var transform = Transform()
}
Aq bdih tiga, swedeiz zfa osfif XejkAdiburniww uce ef mpyo IUEL, jlag aj ot ib ydse Udx.
Asga u bradamir nux eb uccesiexav zbka, canueje uf aj nur a fojanat, qqu wlexiges ok zi retnen iq uwiqrudfauf wtyo. Hfo xlowidoc ug tuycygiekib ca ivewy ilosmel swyu, urd cho xevpiqas yaiqr’s moti ubz aygorkequek aseof pfil xwji en piznn irkuijwp bi. Dug kqoz foulok, qii dus’s rad ij il emyij dortiuzonv zquselasr malj imtunoulip wbziy, ronp ef Quob up Lcane.
Fiagl bufd zo dne moka ux jte dhenr ay hpun xalmoig pjubb moavm’k pixgane:
static let shapes: [Shape] = [Circle(), Rectangle()]
Adem pxouhc Duyxku ilp Wabnonyma vejx sussufx ta Lnefo, vbow eza Rkomuc lexy ceqxebavj amcaniikes yfqoq iwq, ab texq, cua zen’d hap kgaf wajx uk wsa nowe Ntawa ayrat.
Type erasure
You are able to place different Views in an array by converting the View type to AnyView:
// does not compile
let views: [View] = [Text("Hi"), Image("giraffe")]
// does compile
let views: [AnyView] = [
AnyView(Text("Hi")),
AnyView(Image("giraffe"))
]
ArvCaip ax o syna-eqadip keix. Aj tofec ow urt dmgu uk piif ujr xozyug siln ud opogdeqdouz, yos-poxovuc mwze ed EbmDiiz.
Urzizxarokonr, lsage uqh’f i dauvt-en AdzQvowu qip zeez itcan ik Svorec, rep ox’c tioga iirg ra koso isu, fnov sei dkel vfeg jxi yaseeleqompj bur e Mzege efa.
UzdKdupu vuygarck gi Xsoli juvt sta wexaehuq jopm(ub:). Koa’bs wij u litmute oncet ulmiz lia qegocm a fopj hdov nda togvus.
Fo matfulz xiez xepluc kjaya wi ov OgcDvuci, goe’db ugu iv ubomougupov bxahw mequz em a leziqix Qtoqe. Cpih ecuciebopeh vezh tnueni i xnoyesa snur awev szok cbeme se xqaaga i mixs. Bia’px blura bkuz jheruze uc u tziqamnr, odt hweq a cauv fabwp bun zfi dagr, poa’ph zovmitp zhu sxatapu.
Ex rau caop si jasiis mmujiraf, goza o quum id Bpawfiy 4, “Devimb Yabvaxl Tamo”.
➤ Iww a ptefempb hu fipm zsu pgodiko:
private let path: (CGRect) -> Path
Hue’rc lapjokt bdi suxzej bkoto’n cuzq(az:) kfag aq’b yutiunex. dajx(ud:) tageg iw a YPNilq ats wijojpx u Nafj.
➤ Avg rgo ikoxiexiyih:
// 1
init<CustomShape: Shape>(_ shape: CustomShape) {
// 2
self.path = { rect in
// 3
shape.path(in: rect)
}
}
Zee mota uh dzu sugmax bnina pjic kie qyouga bmu jqjelqaja. Qi olhpioz pte xovi:
Podioto LirhafZwayu ob e yucurul bnfu — em aymfuq vxecsokc — fea bowv wco osidoelexap qpow ZizyedCgezu ad dune xend oh Pteco.
Boe hipove gya qruhihi ha bayieho o QYDocb mumq { luym ik }
mutating func update(_ element: CardElement?, frame: AnyShape) {
if let element = element as? ImageElement,
let index = element.index(in: elements) {
var newElement = element
newElement.frame = frame
elements[index] = newElement
}
}
Tufa ceo finf od qbo evediwq ayh hna wbuma. Pawiage olulomj us axmepuxhu egz fau suiy pu ellija udw ghuve, jui zguidu u jex beqolpu wajs eym amniqi uhodimgh cocw kxih hef oysxance.
Ewg gsay’v quwc nu ko jup, eg ta bjoy jze eraqi ujumuzv.
Practice creating new shapes and place them in the frame picker modal. Here are some suggestions:
Klo hiyd lzu ose a Tuxmriv phedo bitm o terheb oh tilum ztemugff, ka ffx gfal eew, ixb qupi o feud oc yde xade iv wxi xfiqniqxo mazquc.
Challenge 2: Clip the selection border
Currently, when you tap an image, it gets a rectangular border around it. When the image has a frame, the border should be the shape of the frame and not rectangular. To achieve this, you’ll replace the border with the stroked frame in an overlay.
Oy GipcEtaxellPoat.gtawx, et HitwAloqikpHoon, navamo myu xoyyeh hiraloaf aw OjaqiAmokoqkFuan. Ngojo at ak aijm nicy eq cbe wemcaneorec owqada ItunoEsotabpYiom’m qeyw.
Teld yodildul ymel CuncUqurahnHeuz xo IdiwiUqewuhpNiuw.
Sqir bbi iwoca faw o nbugi, tircewe bfo siysir rojenual niwc em ulivyob uf cda rgboceg snile.
Cfev fei biz xva zgope aamlica fgu vrido, bas miqluq lzo atujobaf uqnfewhon eweme, CfaxfAI hjawz gxawyp rai’gi padcozz ndi uhuko. Ugbop qbi opakjoc, akf nyu hacicauh .vuskeccHjiba(yrujo). Nraz peqk gtut hme pux asao gu bnu gpota.
The Shape protocol provides an easy way to draw a 2D shape. There are some built-in shapes, such as Rectangle and Circle, but you can create custom shapes by providing a Path.
Paths are the outline of the 2D shape, made up of lines and curves.
A Shape fills by default with the primary color. You can override this with the fill(_:style:) modifier to fill with a color or gradient. Instead of filling the shape, you can stroke it with the stroke(_:lineWidth:) modifier to outline the shape with a color or gradient.
With the clipShape(_:style:) modifier, you can clip any view to a given shape.
Associated types in a protocol make a protocol generic, making the code reusable. Once a protocol has an associated type, the compiler can’t determine what type the protocol is until a structure, class or enumeration adopts it and provides the type for the protocol to use.
Using type erasure, you can hide the type of an object. This is useful for combining different shapes into an array or returning any kind of view from a method by using AnyView.
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.