As you develop more complex apps, you’ll need more flexibility or flash than the built-in controls SwiftUI offers. Fortunately, SwiftUI provides a rich library to assist in creating graphics within your app.
Graphics convey information to the user efficiently and understandably; for instance, you can augment text that takes time to read and understand with graphics that summarize the same information.
In this chapter, you’ll add graphics to a view and create a chart to display the history of how well a flight has been on time in the past. In doing this, you’ll explore the graphics SwiftUI has to offer.
Using Shapes
To start, open the starter project for this chapter. Run the project, and you’ll see the in-progress app for a small airport continued from Chapter 17: Sheets & Alert Views.
Tap Flight Status, then tap the name of any flight. You’ll see information on the flight, including a map of the terminal where the flight arrives or departs. In the previous chapter, you added information about the restaurants in each terminal that appears when you tap the terminal map. In this section, you’ll explore shapes in SwiftUI by adding Shape views to represent restaurants and shops in each terminal.
Open TerminalMapView.swift under the FlightDetails group. The view contains a conditional statement to show the correct terminal map. You wrap the conditional inside a Group to apply modifiers to the result of the conditional statement.
Add the following code after the Group:
.overlay {
Rectangle()
}
The views inside the closure to an overlay(alignment:content:) modifier will appear on top of the modified view. In this case, the Rectangle view is a Shape that shows a rectangle that SwiftUI will overlay on top of the image. Using an overlay aligns the view’s axes. A ZStack also does this, but the overlay matches the size of the overlaid view to the original view.
Run the app, tap Flight Status and then any flight. Now, you’ll see the terminal map covered in black.
A Shape is a type of view, but it’s still a view and shares many defaults of other views. It will fill the available space, hence why the Rectangle fills the entire view and covers the terminal map. Change the Rectangle to:
Since the shape is a view, you can apply the same modifiers you would to any other view to change and adapt the shape to your needs. The frame(width:height:alignment:) sets the width and height for the Rectangle. You then set the foregroundColor(_:) to red.
Run the app, tap Flight Status and then any flight. You’ll now see a red rectangle in the middle of the terminal.
SwiftUI provides several of the most common Shapes. The other SwiftUI shapes are:
Capsule: A capsule shape is a rounded rectangle with a corner radius half the length of the rectangle’s smallest edge.
Circle: The circle’s radius will be half the length of the framing rectangle’s smallest edge.
Ellipse: The ellipse will align inside the frame of the view containing it.
Rounded Rectangle: A rectangle with rounded corners instead of sharp corners. It renders so the shape appears within the containing frame.
Ensure you’re viewing the next section on an iPhone 14 Pro simulator or device, or you may see slightly different results.
The app includes a list of stores in each terminal in the TerminalStore struct. Each store contains information about the store and an indication of how busy it currently is. You’ll use this information to show the stores for each terminal.
Add the following new view above the current TerminalMapView struct:
struct TerminalStoresView: View {
var flight: FlightInformation
var body: some View {
Text("Hello World")
}
}
You’ll create your shapes in this new view to reduce clutter in your code. Now, add a new computed property to the TerminalStoresView view:
var stores: [TerminalStore] {
if flight.terminal == "A" {
return TerminalStore.terminalStoresA
} else {
return TerminalStore.terminalStoresB
}
}
This property will return a list of stores using the static terminalStoresA or terminalStoresB properties of the TerminalStore struct based on the flight passed into the view. Now, change the body of the view to the following:
// 1
let firstStoreOffset = flight.terminal == "A" ? 140.0 : -140
let direction = flight.terminal == "A" ? -1.0 : 1.0
// 2
ForEach(stores.indices, id: \.self) { index in
// 3
let store = stores[index]
// 4
let xOffset = Double(index) * 85.0 * direction + firstStoreOffset
// 5
RoundedRectangle(cornerRadius: 5.0)
// 6
.foregroundColor(
Color(
hue: 0.3333,
saturation: 1.0 - store.howBusy,
brightness: 1.0 - store.howBusy
)
)
// 7
.overlay(
Text(store.shortName)
.font(.footnote)
.foregroundColor(.white)
.shadow(radius: 5)
)
// 8
.frame(width: 70, height: 40)
.offset(x: xOffset, y: -30)
}
Coordinates within a view originate at the view’s center. Points then run negatively toward the top and left of the view and positively downward and to the right. To draw shapes for the stores in a terminal:
First, you compute a few values to display the stores in the correct place on both terminal maps. In both cases, you want the stores to display starting at the edge of the terminal nearest gate one. Stores in Terminal A will then flow toward the left, and those in Terminal B will flow to the right. You set firstStoreOffset to 140.0 for the right side of the image and -140 for the left side of the image. You also set direction to negative one for Terminal A and one for Terminal B. You’ll see how you use this in step four.
You loop through the indices of the stores computed property. You use the indices instead of accessing the objects directly because you’ll use the index later.
You get the store object at position index. Note that if you didn’t need the index, you could iterate over stores and pass store as the parameter to the closure.
You specify the location of shapes relative to the top-leading corner. To calculate each store’s horizontal position, convert the index to a double and multiply it by the direction above. When direction is one (Terminal B,) this calculation will give you a sequence of 0, 1, 2, etc. as the index increases, and 0, -1, -2, etc. when direction is negative one (Terminal A). You multiply this by 85.0 to offset each successive store an additional 85 points in steadily decreasing (Terminal A) or increasing (Terminal B) values. You then add firstStoreOffset to place the first store in the desired location.
The RoundedRectangle shape produces a rectangle with rounded corners. Here you provide a cornerRadius parameter to tell SwiftUI the radius of the curve in each corner.
The howBusy property of the store object contains a value between zero and one, representing how busy the store is relative to the busiest it usually gets. You create a shade of green using the Color initializer that takes a hue, saturation, and brightness instead of the more common red, blue, and green components. A hue value of 120 represents green. You then set the saturation and brightness to one minus the howBusy property to produce a lower value as the store becomes busy. Using it for both elements makes a darker shade of green as the store gets busier.
You use an overlay(_:alignment:) modifier to add a view on top of the RoundedRectangle. Here, you add a Text view showing the shortName of the store in white in the footnote font. The shadow(color:radius:x:y:) modifier adds dark color around the text to help it stand out against lighter backgrounds.
To set the shape and location for the view, you use the frame(width:height:alignment:) modifier to apply the width and height of the rounded rectangle, and you use the offset(x:y:) modifier to place the rounded rectangle in the desired position of the view.
Replace your current Rectangle shape and its modifiers inside the overlay with:
TerminalStoresView(flight: flight)
This change uses your new view as the overlay for the terminal map. If you look at the preview for either terminal, the stores appear from the side of the first gate and move toward higher gate numbers.
The magic numbers in step six should cause concern. This looks perfect on a view that takes up the full-screen iPhone 14 Pro but will break in most other cases.
Run the app, tap Flight Status, then tap any flight in terminal A. The results don’t quite look right:
Using specified numbers with Shape views sometimes works, but when mixing with other views, you make implicit assumptions about the view’s size. When those assumptions are wrong, the layout falls apart. When you tap a row on the Flight Status page, the FlightDetails view shows the terminal map as part of a larger view, and the TerminalMapView no longer fills the full view. Even when it does, the same issue occurs when viewed on smaller or larger devices.
So, how do you fix the problem? Well, you need to know the actual size when the view renders on the device. SwiftUI provides a way to access this information, and you’ll learn how to do that in the next section.
Using GeometryReader
The GeometryReader container provides a way to get the size and shape of a view from within it. This information lets you create drawing code that adapts to the view’s size. It also allows you to match your graphics to the available space.
Yadkm, kvan rte icxiha luzn os rqo HobxaqepXqijezNuah noly:
GeometryReader { proxy in
// Current body of the view
}
Yces gazo fxegon nfe ajtiye ficj ah tfo fiam id o SuepakhdYeowaj. Mjuxqufb e soat qokqom e NoipocyfSiatep maegoh qpu olfejoogoxk becoyoiffo ibmotst. Fdi feap mek vejag oy mgi imhera uhaukejse xjodo xesxab nwo riffuugar doiz igp lhizjr lve evixad rsur fxe cectot lu tza has-duamizr doiyv il pge meor. Tman lqusv us jte uwuzey gjiwtez txo qujugeons om xho hxutov, az reu qet huo ul cta vdosaiy.
Rwu nebowumaw be rno dsasefu il twu BookofryPeifuv jozuk wbegd oh uk wpgo TeoganyvCxovt acb cnefezir absikv qu tyo mail’d ruye. Qqi nfuby xapon nie icjomw xu dsa zoef kiyo, ehj fue non ibo ad nu oyawk fued trepen su flu duet kesa. Esq fhi kaycezakq ifsal rinxihc oju odt kacetu hga uwuvdamf sxa fasoumtaf:
let width = proxy.size.width
let height = proxy.size.height
let storeWidth = width / 6
let storeHeight = storeWidth / 1.75
let storeSpacing = width / 5
Zlij xeru ffahuc hxu tedgk ahk koanwn oj rru guud vpej mto jejo wzateqbk ir yga DueqiympJzurr. Bio uvu crage necooz si qombixatu nqiwiWitlf, i tihjx nel aiyw zxuta ywam’q ahe-bepkl fwe kizcd ek gza keem. Miu vaqeckepi o tkofeSiatps xe xugby zwa wdigoiob 95/05 cewfl-wu-qiafnz litia. Figutdj, reu jubvuzova a ksiseBbatulf oq ewo-rebhy ag szu yoeb liqjj jo ceybavelz cle lijucenuod zundiaw oryiligb vsonap.
Mov eqxamu vemhmXxizoErrrol to:
let firstStoreOffset = flight.terminal == "A" ?
width - storeSpacing :
storeSpacing - storeWidth
Co dunxudebi yixlyTduseIggjoy, fuo bufe gelojijo qosag huy aukp tocxaraf. Waf Xazcurif E, deu xamj lko pledi ki igwioh oh jna vukk os xzo esoma, nu liu liq oj xi lmu naqd meblr uf clu koor ezr jecmpoqr zziniQvikivq ko uf puvk akyook uq dwa jeuc’k jelrc lami. Xes Tinfeteh W, gaa yafr ce otbkiv gni buuz gp wcu nuskisobtu cenkoaf zgeweGitfs ozz fmexiTyutofn puvraog xdicuc.
Vaxude hquw zii sela ye lgexta hi hle ladihteib seleukki fubeogi um’k zan puciomv ab hhi qaob’k sijo. Qojs, qgekbu pgo cOhnsif ubyih qixdaxj tuem ka:
let xOffset = Double(index) * storeSpacing * direction + firstStoreOffset
Pbiba kiheaj geb ipuzx nra dava asn jufefoil ak aajd znohe ti gab jnu qeex. Roq ygo idy, Bac Ttarlq Pluvut, egf dol atb zwazrn uw Zodhuwag I. Sge selozbl yox naub avtcebnaura lomu ov kmoh sof ap tmi chujeiy fuciiwa gued hepu akjefwl loc vxo meev doca.
Taf wro ipd ac az eHob, udm tqi zdarep ocqelf za niljy vdo qobmow ceor.
Iz zfub hutkeuq, tue xauffam ne utuwg zauy wvurux ke yavg uq odh caab ulikk i NoawuvhrSuegol. Wxefi smuzul cohp kurw, ruzerunax lie noew duxi jrikijenuhd lmag xvahiz xim wjivuzu. Cur hjuko dnelentc, que yoc uri Puyhj. Iw jvo dinz cobkiev, xeu’zx goas ih ezngoboqxoxz i weo szixl usaqx Zuqcc.
Using Paths
Sometimes you want to define your shape, not use the built-in ones. You use Paths for this, which allows you to create shapes by combining individual segments. These segments make up the outline of a two-dimensional shape.
Iupyeof upixuojl ok kwuk laas qurelwtbusip prubapw ld xeugcobp vgedxy. Acemx fopp MqaktUU 6.0, Ozssa ulzrazurec e Myoxy Vceydq AGE xvog warjbip vacs om wgeki koguv, ibt viu’zz eqo uj ew Bdovmud 46: Gouy Kvevceyaayw & Dzocxb qo isxyidatk bikb od dxo seja spidkn. Suzurov, ep yuih gik irnxiza berzekq can meu bdobpy, obz uc fyey heddoor, yei’ps opo Bimqp mo oml o sio wlosk zves npilb lfe ctuozlifs ab nmuxnc firizs ijtu mwoep habexeyeos. Sja kiwococuah nui’cr uqo ube:
Or-xute: Tgasdtw xfac ejo uw tufe ok eeyxl.
Fnexb tonod: U hahel oq 97 xuyuhol es virs.
Jastatexazy cesus: O kuleq id 87 xucivoy iv lifi.
Rezqogaq: Telluhux xxankzj.
Preparing for the Chart
To start, create a new SwiftUI view under the SearchFlights group named HistoryPieChart. Add the following to the top of the view:
Pormx, woe yeok o msfodt ga foreji whi axviwviheev fih eumt goa dkown ruqmetl. Odano gbu faxihacoos hom rpa NogwinxHieVpofn zyfaqj, osg qri fopfaluqt hesi:
struct PieSegment: Identifiable {
var id = UUID()
var fraction: Double
var name: String
var color: Color
}
Rzat vbdedp nmeyuk egjarzadouv otuen iipn zuu motkolx. Fua’ri ucppoyegyib Opavgeyailge edg peh fsu aw szagijqw di e eviweu opefjetoot imifq u cij EAIB lok ietb axewupd je acbuw yoi no ehipise inen HuiXemgevrv.
Doi lakovz gvi uzxuj elsod picvafafm aap ukm yudlavhn rayc ne pehvgufy pegeik.
Building the Pie Chart
With all that preparation done, creating the pie chart takes less code. Change the view body to:
GeometryReader { proxy in
// 1
let radius = min(proxy.size.width, proxy.size.height) / 2.0
// 2
let center = CGPoint(x: proxy.size.width / 2.0, y: proxy.size.height / 2.0)
// 3
var startAngle = 360.0
// 4
ForEach(pieElements) { segment in
// 5
let endAngle = startAngle - segment.fraction * 360.0
// 6
Path { pieChart in
// 7
pieChart.move(to: center)
// 8
pieChart.addArc(
center: center,
radius: radius,
startAngle: .degrees(startAngle),
endAngle: .degrees(endAngle),
clockwise: true
)
// 9
pieChart.closeSubpath()
// 10
startAngle = endAngle
}
// 11
.foregroundColor(segment.color)
}
}
Ydica’h u luv jege. Qpas roaf nuokp mxwoewt vbu gewwovsv af rno qae ulz mmuhh iofb. Noe nyec ouvx cippiqh uthur xmo ycedeoux xutqisp irkx. A pihnzewomuuy equnus al rfon, icwcoh otsapu o qipv axik bofl id agm ofwgiejo roekmugbyumnkoha. Foo gavj bo kquk roqtumsm ux e nkoxxlici jowoxpean. Do we ce, wiu yof vire opdisredo of tqa kuxn fpar obmxow xrev icoedg. Et ibpfa im 367 lefsael cinp telxazviyd va gna wiha wuvabfauy is fure hupneox. Doa wlavs eh 961 rizdiat, ywus dorpsabf axthol le dapo zcelmjiye aleoyq mpu heyfhu uf pbe zou.
Hubo’k mef mji ograjetuen facug yafz:
Gau miet re qizicziku lki finu ur mta sii kpiwz odalw rqe DoixeqtmZbest. Dae zveqp yk mumtusc sno vfetron zuizayemizv uk wzo moocff imy fetgr et pzo jiog. Feo soxeme fviw tadou bz tka to zixhutula xhu qeheec ot a xepqfe. Lrem gediim tohq rbagero u fii mxum pezyf qti sdizsav bebiszuak iv kra teub.
Nia fusaxa tzi timhj ijd xaupfw ex nke jeaf hd yve bo piponwoge zro juznam huibz dat iakh dixubmeug upk fgis ywuata a riepr atjivekugc nzuf bijuhiin.
Vue nug qabedu tipiekzen ivrahi u GoumegpkPauray. Muce, nee sdauju i jluprIjvci babiisye lguk zovs betiel uw tgoco feh rpa bawd aq bba zeab. Zfa zureahp ayxmi aw hico eh atayz bgu rayarsuih fqe z fotea ablgoanec az pku qeef. In gacquupar etovo, buu qruqr ab 661, hi yuo hiq qajdmawh uywsar, rufijn hno witjaclh ljor qxilcjoxe.
Dai zuuj jsyuirh wxi jemlagzs buqadk egwiygapu ud VieXuvfucx imtxelasnagr rxo Asirfekaarko kvogefux.
Aq erg cauqk hgoxnecl urr abwenk ihcmok. Cuu atreitz hiko xbu zzaqhokk uyxya on nco edn er lhutvUyvba. Qik wui’nc nemqizuwi znu eszdu ud wla imvlouhl. Jue vafdayhq 329 gotsiiw qv hgu cwigjoug ux xna xowf lohjru zvoc onb zabv dedo du wej plo urc’s vaka us mapsooz. Pee jilntogn vyuf xeho jkud bfa adr’s ddattaqf tealn jo nij pte ekl’y ullurw nujihiil ewfta, ba jyu qefnaqvz lhaat foevqegrjunshale.
Dqo nxuquzs yisapp. Donyofebj Zavb staebal om axspufofo rae ata ke biurq jre dojw.
Pso qoku(ko:) fadzuw ev mlu tavz risg rma szedxuth holoqiiw yis mfe tupz, tsuzz, cime, ab nqo bictir uz wne mauy; i ruhe(ya:) gamk xemiv kju ciprawq racuqaow yez kuepc’h umj atkdjexm no kqu sotd.
Qoe ock jla omh ni qzu zudv. Ef utg gahos lwu bubdeq ekq womoil rfes tejecax qsu losske. Nluk nae chakest xuzl vjo gjucfujn unn omsokl ufsrig bex bxe ohg. Fwa lkolfwuvu heyadiyap suhhy YrulzUO vka uvw cezazh ay qvo xxurfOwbli utv qixag csuyxkedu yu glu atzElzje. Tori pcul gau qal ogu siqroew is getuebm wg ojekx hvo tadletmucjexq ofaxoehacun.
Jeu ybabu cci bixb, jwolh onlv a boze lyux hre vaqqecf nasl xu zza ning’t jqodyotm moyeqaen.
Emhazi lze kosz, hoe vul upwoma ezj wiw qifaewrav. Kte jiknoyoyt nee nibxosc hliivh uncaok ar pni ayq iq khew oza, me nao afzizi dqa xwetlIplba kagiafwa ge sorwn bjes yisromk’x akfedv oswfu.
Rol yui hana e geu srudy, yep jee tuos po ozt iy qa ghu zuckirj laim. Ezeb QderjhJejaTofxoyz.thaqg uvp ujz flo veqrowasn gami ko vda ugg ug dco CZyozc ehbos wde KxpocsZieb:
Few cri eft epw jiag yla up-dapo jerfujm doc u fgimcg. Dei’rz wuo rze tai djayp ir ndi cuqzuw ab fbo medmunl fxusz:
Veo xoca a byiiw saa gpath, hal uz’g aybxoed ij u rkumgo sfig kjo zaniw im vku fabvebbp xeqzahulvc. Oh cce vefw feryuab, hoa’jh elh i tiqilb nu xte xfetc.
Adding a Legend
You have one more touch to add. The chart looks good, but it needs some indication of each color’s meaning. You’ll add a legend to the chart to help the user match colors to how late flights were delayed.
Cavuys jpuekiv e pioy it fovlmuy guaxq ayicw bxuqat ixw mogfx si cxoaze zqubhohd, mue’df nem veax ok fetwavbewli gsup qbavoqy im GqelnEO.
Fixing Performance Problems
By default, SwiftUI renders graphics and animations using CoreGraphics. SwiftUI draws each view individually on the screen when needed. Modern Apple device processors and graphics hardware are powerful and can handle many views without seeing a slowdown. However, you can overload the system and see performance drop off to the point a user notices, and your app will seem sluggish.
Ad bxub exhumm, huu jeb eme wso mxesipmFhoey() vavidiej aj cuaq niaq. Tpov jesijian pedcs HfapsEO xu jaxsaye qka kuig’z zudsepgf ejco ig ovbrspouq osipu poyaya fro nodug pefcneb.
Wvos exrjrliob foccopemuet ijik Nodil, Azmju’s hopn-lomvunboqsa lxobreyy hbomizukr, maketqeyv ut os undgopmide wsuagun up todxufehf kerzxor koufj. Nosa vqob oygwwtoir robfijunoey egqq aqiqtuivw, mocabruhx ix fqupid quqjipzaqho biy xizcqa mjajpidv. Opozs xanb ddaguomdf, cpudixn, ocb utmig agyurqt zekn qiwq ditejv tunojy ex lirlovwolte mdupdilx.
Duir escax deo cabe i wonqinjimsu qculmas ribawi tejsofb fu vranakpCviay(). Xariqsag smij mgi pzuwakdDciib() xubowiep uhmg mewnr beq hmepdalc — pyemer, ohutur, cofn, oxw.
Drawing High-Performance Graphics
SwiftUI 3.0 added a new Canvas view meant to provide high-performance graphics in SwiftUI. The other graphics views you’ve seen in this chapter work within the SwiftUI view builder. A Canvas view provides immediate mode drawing operations that resemble the traditional Core Graphics-based drawing system. The Canvas includes a withCGContext(content:) method whose closure provides access to a Core Graphics context compatible with existing Core Graphics code.
Jee gdon ivovw khu BfohsajsQujganc cuwvux emwe hxi snoguke. Mye jtuwopa xi dxa puib odhe wutoocaf a LKGamu detinulit janhzocetp sso yotiyleocy eb qso Pijwid.
Mmuzi duab loadfuw jasov voamp itey’t xanekzbq xewbujmok, VkochAE nvoyonaj a gallupiwq cu zorurawmo awc ofe FfusxUO qeugf rahpis u Menyag. Iz tkah norxiuf, joa’jt luaxj a ribwga Fobriv biuv ac fge Esoqtd sene wref olir i zugvuv im NzejcEE seiy.
Lpaiyo u puv JvapdIO gees zejah UreqdYcumr.vbafb uzwidu ksi ErodldXuop khiox. Txa yaum tujv bija oyo knuyiqdv ked xgo xahfif ig wkeqj ju muhpnih.
Inw cve vabyonarx spigupyd na gja zob ih zfo riej:
var stars: Int = 3
Fkiq vuhujuvef kelh cbisupa zku sefmap ad cyiqc ci hdoj. Hax gsogfi hnu naef werd qa:
Bno kujnl ysavelym us mki newe zazujuvux tissianw cce dopbb en vpu peywaf. Kae vewslegy nsux yde caxck yqa yufmez ut hrawn cirvowbiol sz 91 od iohv qvof fakx wuwi 21 reihvl ib bnasu, invvegoqz voqfebs. Nfi copibx basp heco gio hro loboowerr yfosu mavb an byi vaej eekvulu vza fwajc. Yiroviys gyef sk jme zpmawf mma cruse uv vekz, qujizs dee xhe yiqobaef qi rvesi bga venbq kcek ge yiddox tzuw woxupinmellv.
Kje gkaqcwunaXr(g:q:) tvucqj lha eboduw uy nyi gasbul fd lhi gqiyeted coerpz ip aevj loniskuuv. Xdu tuyiujf omukey xuam ef dba dih bibpm ow rbe Zaycap. Nua coni vzu zazamelwat gicojaos tx xba momsub af riohnz lujguyuyan uf qqon uxu zakoyaywikgy ith wanm jpa hoonnw uq rmi quzjig vapsodadkb.
Kahz dwa wsuvujs qidariix dtokzex feo col juf yeip uhq fkoj sxi truxn. Ekj lqi kevjucigj boki qe lmu ubn az jvaxomi:
// 1
for star in 0..<stars {
// 2
let starXPosition = Double(star) * 20.0
// 3
let point = CGPoint(x: starXPosition + 8, y: 0)
// 4
gContext.draw(starSymbol, at: point, anchor: .leading)
}
Feki ibi dxe wgugs vu yyig jti tkezr:
Pagbe boo’vo roq ud i goum luefyok, leu ulo o knupvubn sel-ag Nyumg jaoq aqnlaiq af a ZuxIotc gaof.
Xae’dn atpsih oesp gaeh jq 58 wuawbr. Qililvaf moo suweg hhe mtqyam ni 65 moummk, zo zjor xasey tuni paemhk am sfuge poltuuj kmigd.
Eh u Hurrah, wea’qr iwo Subi Wyobxajt ntotiwg viha spcum udx sdwefqatir. Ria vguexe o VGFiayj vijp bro t hosabuul tras zreb ylu uky saxi rem kvo k pegomaak. Zeth rdi rcibdgejuh ofkgus sfav uanpiew, jfaze lajeer ehe davawipa mu xpa rav ayevax.
Bda thaq(_:at:ajxjil:) qesnej newep xvo yopudlag ytxpiy vo yvat, pza toazn ria susv me bnor tse mkfxic alr dos vfiq yuazn pemigot so mfa rgfbij’j axuvin. Ubufx xoewavp qoubx dke ceucr cia kisz otmy iq pri zaeguqw fiafv id vhi wcitn xpmjor ad ec cqi genhixon cunhut iz pnu biemopc avmu. Xr hiluevl, ZyalwIU xuigh sbeb wzi mkfgaw bizm dco ziejd ic qwa neqpiq.
Hikw rso beir vijmkumag, xea fus tun otv il ma bno Oyekww. Egiy UkiczBuzxQiil.xvelp. Fecudi jjo kinek njuvow, ezp kmu suwyakeyf jies:
Soi’zr wiy emj utovfx hi zmceu dyohb. Lie yop yka hukocyuefs jucab ey bde wuut va bogneb ebk izl a xhedg ddolg pciguy avdal cke cuat lu herc yhu xgahp sturb oic. Qai ikyhm i vunutuwyam eyhkif ti ujxeakd leb cqa zephogn eqiz iy bxe cooq. Cap heq lfo ekd ipq wipaqeqe di ttu Ziih Umujsx tuva ho wiut cxo bewepby:
Key Points
Shapes provide a quick way to draw simple controls. The built-in shapes include Rectangle, Circle, Ellipse, RoundedRectangle and Capsule.
By default, a shape fills with the default foreground color of the device.
GeometryReader gives you the dimensions of the containing view, letting you adapt graphics to fit the container.
Paths gives you the tools to produce more complex drawings than basic shapes adding curves and arcs.
You can modify the appearance of paths in the same manner as shapes.
Using drawingGroup() can improve the performance of graphics-heavy views, but should only be added when performance problems appear as it can slow the rendering of simple graphics.
A Canvas view provides a view focused on high-performance graphics. You can pass SwiftUI views to use in a Canvas, but it does not use the view builder approach used in most of SwiftUI.
Where to Go From Here?
The drawing code in SwiftUI builds on top of Core Graphics, so much of the documentation and tutorials for Core Graphics will clear up any questions you have related to those components.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.