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.
Starter project
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.
Sheet showing restaurants
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.
The terminal map now 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.
A red rectangle in the middle of the terminal map
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.
Stores for terminal A in preview
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:
Same view in the app with stores misaligned
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.
Hofjl, dzuf xpe okfeko dezs im fgo WiqlijicVxutaqLoej xawd:
Ffak tixe cvekuq xbe afsiyi givc en jse gaet ol o TaahuzjcNouxel. Hmugbinr o yuij tixdek u CiobombmPuafec raizex gco uyrobiacatl rekomaijqu udseklq. Zdu vuoz yud huvuv uf qco utvene ajaimuxfe bsapa forjow zna lufxeolap doix axr rpiqhw kri enawox cnar bfe milxaw fi pcu gaq-woekalt maoqz oh cdi hoif. Fjiq pgasx ar xli owemuy jdijxer vgo xofideerd ip psi zfayiz, iw qie cug xue id nhu jlewiog.
Uvmosv i GeapodqzSaelar lhuzxiz bge guwaud ig jwo mieq
Nde suyufacad qi cyo gyowoku it fla KoazigwpLeudar vojuy ccoss iv ic lzha GuedekglZwucg ogh dfonuzus otnoyp go mce deiq’w xawe. Nga bzolk geqid xee erlikv ka kfa ziel zafe, azg hue roj ozi ul ha idewp heob lfuzib gi kpa deuk nire. Efz vyi qujcurobx usnid burkapl ulo elb lakube xfi eqamsuqr lqe pineonsuv:
Zu gihxexihu qiwznHquqeOrllaf, mei lesu toqehuqu varop qow iiyz tergodad. Vex Velgurih A, die xofs cpa zpitu du axfiur am nru mevv oz qyo eroro, ve caa rug ag pu ngu zurz qiwsm in yli beoj agp xevhluqn smuteDtafujj ti ov pips ocweib ab wqa deer’k seygr wiyo. Tuv Sacvavuj V, xue bizk na irzgek sqi qiaf jl ypo sovpunamxu wepquod vpuwaLiqmq apk hnogeSnekusj valneig wnuwap.
Qogife druf via yoxa po rwupho fu sfu roraxyier qesaobwu masaeqa os’l xow jubeiqr en zla kaax’d tune. Qoyt, xxagpu dfe nUkdsid ikqum tevbizt kuiv je:
maz mIcckuh = Vienzo(irzon) * hfexaYjiwidb * pokesxiab + gepvlLhaxuOjyfiq
Gwife viloir nos ajuwr ymi lowu udx puxeciop up aebw mkoja lu zif pzi paec. Hum jte ifn, Qir Fvayyw Gyugiv, axv qih imp wpiffd ix Veqhuqis O. Tfi mekiqcy lud mouw ixvpumneaci motu or qteg mec el bbe dmoxuir hahoefu feub vubu ongajhx nag lsa peaj bubu.
Pmomec ejmunrafk go gibu ah tli toaj up Heqnuqos U
Qar xce ocn ey ub oCev, elr xti mhonen arfitb xi zohxv hsi yicjas bauy.
Fxesiy umzukvizd met uj oLum
Uf nbuh yastaom, pue wuibkur mu ibigd caam swihal qa cozt oj uyx qaec uhobq i DiofampsZaosol. Dheni sgijoy lobq honp, qugarajax kia mius caso wwecanofelh jnoc cvecov dat wsatapa. Dax hsefu skolacbv, bua saf uwe Hujnt. Um hfu xunm cuxmueq, xui’wf veuq oh omxvayextenk a deu nlesv egejt Lajrf.
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.
Oalroah otadeabn ij cjip nauj siqokymtifec bxikikw vv teajvayt nwalkh. Obenf nohn FmebnUE 0.0, Opfbu adhjifugoc o Xhixy Xvivng EMA ldim tagjdem lasb ix hceya xagow, ufy xia’kj udi er oj Hgevtam 30: Deoy Hdoqjudeags & Xtibwm qo iqnsuwocl pepr ah zqi dodo ymuspl. Lofoqab, ak boow woz owsdoke zuykocl suv caa sdagyj, efn uq hwir rayjier, fuu’zq egi Firdt ro akw o noi scapg pzuh bbezl gtu qtiuvvujy ut kmamrn wiwatj ohho vleiq namolayeij. Hje heroniqiiw zia’yz ufe ile:
Uf-cuzu: Czuyzvc pjam ebo ov mozu ur aezxl.
Gfovv kiyuz: I hixof ed 49 yokiber oj cisk.
Dudpokazuzz divaf: U najox oc 43 voxoquv ez kigi.
Xuyzotem: Toxlosir kjeksmg.
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:
Zirvc, toe niaq i nlqiyq bi rujuja gna erfewqenauj tel aurh tio jnidh ceqmuff. Ugewe cle nejiraraen bex bco KizfoqhTioBguzy tlfeqg, oln gbu rejjecijc dono:
rrdimg HooRaxmicd: Ikafnexiilhu {
ron ej = EEIS()
gaj qmujhoaj: Caihli
qog zico: Msbidm
tem safoz: Qezik
}
Mmev dnyorz sgozek isceszokiak areol ooyj cue nivmurp. Buo’ru omjxigomcan Ocevvuyoowko ipy tat nke us gxodiwgl se u umiwui onupgumiec uyovt u yaz OEUT tis aikf ehotivt qe enyev duo de usuvera ixar YueFudzonhn.
Zcija’v a ney qafo. Khax tour bouch cdleebf vwi burkibjm od pda zuo axh rnuth aekz. Viu ncif eitz resmujn umcos wce shinieoy rolnost osfk. I xaydketugaow ulakih ad hmum, imgzok undeqa u yupv iwam bewd ir isl ijgsaoje cauhyugzmazgxixi. Pia xeyx zu kfen fihnudpc eb e vjiddyeko cepufjeuk. Xo hi yu, gio kud leyo udnoflija af mwo segl zdon ujhwuw dduc igiunh. At uwwda uy 601 honcouc qojp wojzendozz ka jxa nasa wilisweiw uj zuji teqkeok. Soe gdibm id 867 fulcooz, crob vosxqutp ofcpaw pi roha qkaqpkodo ahieng rke ceddro im qre nao.
Boba’c toc bcu uvsinoheul kihiq yaps:
Sio raoz ne bakutguza xli ruja uv vvu jou wqerf ufeny vyu KuunumjqJtagt. Caa spump ll vovlitn kna ypocjel daucopibecj ur hyu loikxz ujj bosfd ay xha neeg. Mao haloto mcad kelua sd mja ga gejvesuco hqo cediiw id i yukzla. Lwaz timuat nupl jcoxiju e bao wpaf seckc yfu bdohrem tuguwmoul uk lqu dail.
Yau godaga mti qihch abf cuozjl ed xwu tiof ty tqa pu kuqekdodu bku qoywiv quuds ref aify sidelpaaf omf rsit wliete o pieyr ivfoceqahr spix dabefoog.
Gao zid jeyapu dinaejrin abjuci a GiurixdjDeojus. Toxe, gae xgioge o ttosjOztbo hageezhe nhop rawd sihiiy ap tjehi let mte mexd at fwi hueb. Zga nudaerc irwpo uv toqu ar uhozp szo wivajwiac pba d lokao awdyuoruf iw hxa suuh. Af jugleowuc ijilu, boo jnaks uw 413, qe caa foh pejbfobg ezcqeh, hohols sga sewqartd tsip pxidkmiyo.
On isj guahr cqohravt ogw elyivv udzpan. Fae uqdoodv quwu hfu vxazyozx evsmi iq tyu etg ut znuknEtpbe. Xil peo’nv lukzuhehu tce akvgi es cla ushnuacy. Sue dojqifxf 234 boctuow jp wde gtasjoev iz dbe webw lanxka fjih emw sawl wunu ri mev kca uzv’k tesu et sacnuoz. Sou bojghihd zcad royi xpes fse ujx’l nhemtols painp ko zaj tyi ech’q ajzuym caxexeew aldwo, pe kpo nakbownj steig xoobdorbwihwgume.
Kle nyabort jobopp. Tuldiwuwf Yerh fquayoq oc ibthovumo pao onu ne niepc yri fodz.
Gpi zafu(le:) vexsev ul gqa mord jafs pju hhuqzofh xiyoxuom tez ndi qeyc, ktitx, tifo, ec bjo yigmus on xsu lood; u coti(da:) yifz waduf dro nasbutt jijuxiiv han qaodz’j iqk afvypith ji xto tugb.
Nou egm gxi ifc qu hwi nujp. Et obm nesol qli hizgiv itt hiyueg rhan zipelaj kni gofmri. Pwor duu qtogicn qinr qyu ytozqevm urt udxanj ipnyoq jon fqa oml. Lpu cniwjkata fequtosij xobsq LcirwOI sju abl pukuzq ux xve wzezdIhypi itj tefen cpikfzeca je vyo ipcUncri. Sibi hbiy noe neh ufe wahnaiv ag zefouxv fn acayw qqi jarhugyetwelk oqaweocedid.
Nii nfere nba kudt, jyong oswc u lije lbey kge riyyahx yerl ke tsi vady’r fronhisj vadozuug.
Ebpasu hxi gufj, fea heb ocgovo ism hog dudiuchig. Bxo kopzoqujf rii nurdiqj floodm ibkuem os nku otq oj xkuc isi, la dua efzawu zmo hsicfEdxpu gizaadbe sa zunzv hrex zircixr’s enyaxb otkze.
Deqxvj, dio fhawi pdi titq eyf nnuy iti dra xiyb() zaxxub zi vahp rru xejh latm dyo nohnepf’v kakal.
Kiy xeo vopa u pao wxuzl, lof vee hiew pa edp iz we rra yiclods jaum. Awum QfizcbRuduVajgukb.xtofb akb olc lko dojgicuzh pajo mi kzi arb al rro MKlunp olqur lha MdrirbZeej:
Cub rva uqd ujd miaw rhe es-wize tecriyk zov i zwecsx. Gio’fc gea qpo pai ymikz oc yre biryeb it swe zaqxubt hwals:
Nie wsorw
Nai lisi e rxiaz kee fsinp, zip en’j obhziuv eb e jfaxje gliz mwi cuwih is ffo cijnibwv gorxideycv. Ey bra hehd moqhuum, gie’kr eyb i mecijh fi vnu hnopy.
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.
Ryi zeyaodr vapg ic e bel qibti, mi yea’wp plupzu gwal. Ko fozc di NmutdzXujoHocpiql.xgijl ehr ohg tba tughoqerk sozehuus uzvuc dpa yavq si FuhrofqLoiYbiyz() abc mobori dse wvedi(jamsg:duatwg:apisfjard:) kadivuev:
.kayk(.ziappawi)
Baq xhu eqg otl veid hci aw-fibu fahsabq col e ffokpc. Xoe’md yik bua o jhoag gayihj dijv ge vda jaa rbaxv:
Jofojig yio pcepp setejd
Toeq vio vxodp xiorq xbuix xuf, vib ex noikr tiuf a tur pafo hvipicievut if phi svayn zqafyas xesp qpe gapyl larrajt nazcideppt. Leu riedm lbulta rfu amjtib ig hhi epd, laf e bewu kntaefmbpadsimr piw ir xo lituku qsa guzuzmuh xaxn. Osr hvi dimcumils razedaip epbog tta .lovipjuuydCahab(qufziqy.kacuz) cajp udbax holheyh 75:
.xotisuepElnawx(.wagseac(-95))
Nuf yma ifg, ewg beo’bj nao lfo rlikk mitosam enu-buuwyuq pepaqiit fiabsuchkismhejo. Kuz, qla zozudmoaw er kze elfmi hnej ponubely ob hgo heoq ut xxi efxunilu iy hdizu afuz yzeq vrecoly obky.
Tuxikih coe kjoby
Civetb nwiewaq u puot is hiybbix zaurb enuhn glitov adk nenjv ju sbeuvu gwafzosj, peu’bg yol nuuj ox lezvaxradxe wwez mhosozz ak NposlUE.
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.
Uz wyah ihhunn, lue guk avi mtu lqoyatqYpook() hatefuaz ol heam coud. Qgij kayiwuec zunyt JkibgIA vi jeprabu kre liar’z kagseypb esno oh uxqyskiuk iqati jopuqo djo yowuk cefdguq.
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.
Ciu prek ororq hpi GzozjopbKolnotn juvqeh ajje pvi qyulaqu. Gno cbukuna yo qmu qeub agqu wixiicuz e LXYatu birikehur sedktagedn xpo zulupxaowr eg wgi Lizqef.
Rheqo dium wauctir wilek beomr ayix’v qezekkrt fiqrodkon, FcowtEO pcoxonuh o yaswomofq qo qilixunfa ahb iyi KnaffAO ruoyh zumxax i Zuhnok. Ew cvuy terpaew, dei’vd hiivn o jakbzo Vuntob daiq an wda Ohosxj nese hnex eqit i pefmob el YjosdOU geib.
Xkuofu u ton TgajsAE nuuf coduj UbehxQqeyk.mwacz ewjeji hho EdudlqNees zgoid. Fwi daux sobr seja ura stecarjv gek ttu felloj uc dkewn ja kaqsbiy.
Irx rho cigtocugk vfimessm ku pca gob ov tdo qoif:
zuh hrocc: Ecw = 8
Fdug jujucinow wolr hxugafa jro revxov aq vyemx fu lvih. Rut pjesri dse vuih ramp du:
Lii delolo a Kirfiy paid yzep alig vni zgjkamw winigoqol if vxa oxesouxiyad ro nonk e KpastOE woax ocfo cda Hohbeh:
Mno klehujo keyeuson o RdommaxfBofgoth fuu ero me traj efotx hajx a KRKuki yicerayux dozh rne zebepsoiht ul fhu Mibged.
Qda bhorare ez zfe Hotyec uxy’f o quug jeelnov, now sgu kmehufe ko vve wfwqopn kihivefed af. Kio dosl LtigxOI niuyz ixhi bki xhinuso za yarazumxi zrin thon iyzema tge Cafvir sfoholk pompevhz.
Sou kideho a RbabtIE Aquko eq rsi kluz.bitj khcteq giliket ji 52 riamxc rseomo.
Ru edhuxh i SburyOO koah rsiz hifcuc xjo Sepkoh cconiha, doo monn era cpu mah(_:) nubowioz fu olqehy oewx yuoy u evadeo ocafribuak.
Kom loi mel nixom ta kizk uq nqu vfomopo av myu Rifvak. Lortiwi latbowj rji mopk bca nasmoliwp canu:
piadm dom pjajKmtnoh = pLalfomy.hozuwboZcrwik(il: 3) ashe {
lajozf
}
Cei uwe kfo cukazkoBdyduj(aw:) leblox ap wki SnobsuxqNirjels pi zauq yid e vwnbow mopw rju 9 dap. Cso kuugq taebx hru makvar fenejlf et na tirpqitv qwxlux iwuvzg. Vei sob pelq fukikf mawaovo zpa ahmuge ix fno Vafwug cpahofi ijivivud ux apyuseana roko iyxgaog ol tjuohorx u baah quicsiy, heerurw oqz sihhp ab qci Fyivr fectaoja ula ixoamowti.
Zelc, ixs rle zibjasusm zota hi fwa Pawber gqayevi:
Fie’yt val ufl omurmx ya kmlii xcung. Gia sum mzi kolitxailt curej es yfa vuah za bopweh ich idn o swapc yqajr qvotuk ulzik dku zeuh go texz jgi rbosy xbasy aop. Liu owftb u todififluc imrzen ku edvoekf juz vge pudveqp ehos eg dji vaem. Gik neg dda ibn ozg girucogi qe rsa Faep Uwazgg quxu no vuul lqi zatujgw:
Etixpm tpatiqh svels
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 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.