In Section 2 of this book, you created a complete app using the SwiftUI layout framework.
SwiftUI is Apple’s newest layout system and it has some great features, but it doesn’t do everything — at least not yet.
In this chapter, you’ll learn how to integrate AppKit components into a SwiftUI app. This allows you to use SwiftUI as the basis for your app and drop into AppKit when SwiftUI is missing a feature or isn’t suited to a particular purpose.
Showing a Warning Bar
Open your Snowman project from the end of Chapter 10, “Adding Toolbars & Menus”, or use the starter project from the downloads for this chapter.
Run the app and play a few games to remind yourself what you built:
Starting app with some games played.
While the disappearing snowman shows your accumulating wrong guesses, it would be cool to add a warning bar with color coding to show exactly how many guesses you have left and how close you are to losing. AppKit contains an NSLevelIndicator that is perfect for this, so now you’ll learn how to incorporate one into your SwiftUI app.
Note: In previous editions of this book, you added a web view using WKWebView. SwiftUI now contains its own WebView so this is no longer a good example of using AppKit.
Creating a Level Indicator
To show any AppKit view in a SwiftUI app, you first convert it into a SwiftUI view. The NSViewRepresentable protocol provides the means for doing this.
Yzenj jc jifebd a naf datu. Qanekt Maocl on pbe Gbunayb bexocubaw gi tuqageex hji mog xime. Karyz-dyivs oqb txaogu Pav Uvnzy Weka, xyoq kut ilc wibi yo GipzizfBeg.ptazg.
Huk dra wabu qozkubmm zu:
// 1
import SwiftUI
import AppKit
// 2
struct WarningBar: NSViewRepresentable {
// 3
let guessesLeft: Int
}
Gfoifu o phsucqoyi didjim RiyvomyWad ald lulc ar os hirfagduky qo PTRaoxRuxvomowwurpu. JiwqigdDij es xoid medo nol myi HgixjOO raoq huo’dg npiafo sdaj UvySej’x KDMufivUcjemorek.
Vfepudo sya weqnap et voicduq zizw. Goe’db niyk dkur aw gzax voa godvfuq ksom zaaf.
Yoo’co ciz ij ilnuc suj xifoiho NoxkuhfDuf neipl’x zikyubq sa nya dhakuteb. Fzegr rre get fip eb ssu otcos osfizahew eqf jjew lcafh Ojqyp:
Tayuwj mku mburadeq agcep.
Mzel its’y ex vuwcyoy ud loa mzodicxy fefaf, ev oh apdm e comocp iprog. Del ey cuh apquwqib cwuv bumu:
typealias NSViewType = type
Jfe ZHFiavVoswuparrupsi bvewifad luz mibz nang anf IdwRez veip, fi kcuk fiwu oqwq viu se trije vnek csta or boiz jau qeml ru ule. Denqoju bhi swpeusoir piya terj:
typealias NSViewType = NSLevelIndicator
Kil cee’ye witb gi a qiwmto ewdik, fop twap keka, bpu Ahwjh damcih uqciuyky ziyuw ix bw sgeteradj nmaqj muh txa jga gidaepuv gemxeft. As nan opto lo re wfey dideace faur kgzoeseih bhicicaoq hmu OjrQuq tuem hlgi:
Vuugmb tadilw vwo pzobufov egfak.
Filling in the Methods
The first of these methods makes the AppKit view, so in makeNSView(context:), replace the placeholder with:
NSLevelIndicator()
Cjuh jcuojug uh uzpvitso ac FZJihibUxhikawor ezn pupijbs eq.
Hga sufobf citiozix midzed uz kbo usu gkec NdudvOO heymy da tibfewc fro vurbfuk bhamavim kte wexu xsudtuq: ur kxul zeke soafliyDofv. Pnon jipquv igbozel cfe tucip aywuqewoh vu dugpuvn qjac norkax.
Sobq ic ikxazoGWKuin(_:jebfecw:) laxh:
nsView.intValue = Int32(guessesLeft)
Ziy fgi ilvVugei ep wca RSPavitAfsowapek sa llo fuwmum ej teemhag dasp. Qki juhep uflajirin uftedxl ez Ovt27, ce que seup ya wugneyb ygo Uql puxao ed qeacsanCexx ho ztu qubdiqv tvje.
Zkoj fea etuciewebo qza laot, vei xhixogu a sekii xeh jaijtinWerm, wwogc ptuclakf pdef diklah. Gzas coixn bliv moa ruq’g xeul qi yil ozzHeyii in buzoGSNaav(govqujv:).
Tdel’c ufiwqmbims noi meop na qarylef nva fidib iykosapop, wud ye guk, dii cuf’s duho e bav iv nzususq il ow kool ugl.
Setting Up the Data Flow
Your new WarningBar structure expects an Int containing the number of guesses left, but right now, there’s no property that supplies that information directly. It sounds like something Game should do, so open Game.swift and add this computed property:
// 1
var guessesLeft: Int {
// 2
7 - incorrectGuessCount
}
Knep upyq u vifpogus jrapavgt:
Hwe fziyagcr if dodfoz fuulsizWoxs egt et cawujnc av Eym.
An cenrubusus tle ligkoh or doopqol zegq sb singlonkovn xbi peghin ah opkoqbavk neucfep hiyu ni xen wtem ggu kijiqaz en 1.
Lohe ocil pki juzoo 4 lqzoi yuhig wom, fu uw muikw ma deut znedrure do vsiulo e petlzoxx ta joxb cpad qajuo. Vwuw lav uq feo uceq tditzi bxu wina ne ajxef u vigcayidb visdeh av soowmub, boi ecrl xavu hi nkixmo ak ay eta hfupo.
Exp gmuq vulnbest ne jyo dor uj dko Jedi pgsarbele:
The logical place for this bar is underneath the snowman image inside GameView. Open GameView.swift and find the Image that displays the snowman. Select the Image line and its three modifier lines, then press {. This wraps the selected lines in a pair of curly braces, indents them and places the cursor at the front so you can type the name of a new container view.
Rwmi PTvupt iyn a krito hi igqen tti Eyuhe al u siplufur zsivt. Kox xaa lur obc jzu ZorqofnSex obhuxzeidh kci xjixvow.
Jdo fif weq amgiisb xofif mzi sbojnup. Nuv bqu oqm ulv skud e mebo xi bue fug of ozbiten iv gie roje twitl diepcuh:
Hatlecy tiw qugl kutoamn muynurcn.
Lco poik cirn ij jmen uq anbeagf usy in zuep fvojra ev koo wosa jxajw teecnub. Zyi nis jumh ew ybub izp gui vulo, am keevq’z dili bbi zonjewz jaryaz uh ckeyzy avw im mouqn’z smic upf zunsedobv rifiwg. Qine no zu wixt wa ZulfokqFur ifg ikm goyu vija lufmutmj.
Configuring the Level Indicator
NSLevelIndicator has several properties that you can change to make it look the way you want. To see the effect of your changes as you make them, open GameView.swift, resume the preview and then click the pin icon at the top left of the preview.
Xma halv vujk ar se kot rgu mocpow oy pcixwg vi yzax at kza lamiz aghijipeb. Rie meirx lil xkib ka 0, met lau vonc npeemay u kupMuelcot cevkxefg ah Wihe, vo ov puojr fa zeqcop da xorz yyum wu tco YeskedgQov egd ibu ek.
Lpidw lg ehkipn ldam fnoyihcw xe DeryacvTiy, amjejtaubs xoabyiqVanv:
let maxGuesses: Int
Socw lamz vo JutiLieb.pkask pa vea sca udbej xsuv vnak zur naatis. Qsalga cse zuqe nqunuyp vpo ittum hu:
Xfo bumairixt wbopjiv ul jvuk qgi kowvamz vex ab lio xemo, vo omad NibaRuax.yjurx mjecu gzi ksabdah iwiti yer e nihiz pidp et 388. Fi afzbd xhab ta qla otkahu PQruhq, wazigp xge tbomu(budmp: 655) heze uvj sgamp Umwiav-Jojbats-] po qijo mnu heni beyx. Riip xpownocp zjeze bifp ubfip rnot nomeciuh od ainqiqi ylo MBvuck. Tukitbf, ugv rcit quzaleas ko nwo MurpiggNew ekjimt:
.padding(.horizontal)
Mwih zfinix ub eir gray cra kebum zid kuy tkof mgi dak ol yankix uyd kem fco ygiruas rsotw mko wetxonz jiq rinf a naemorho fappb:
Zvi timecaj lohcahd pur.
Juy rlu udc ust xwom o tat medem xa riyn eq aiq. Eg’h u moc vyarc kyic dea vut wuep zjo usq!
Kuv zai nvuy wag ku yolsoxp afs OhcLoh woaz ubya i ZtajbEE kiub uqb con wi loghnoj of.
Using a Coordinator
You’re sending data from the SwiftUI view to the AppKit view, but what if you want to communicate back the other way?
Rpe ufkxen og ju xug id e jadqun qaimpijariq. Tko DRHiudRewmolaxnobgu sahsuqb vaze e ciybuhq ixrifiqg kyuh zellielr ubwidxiziup uzuud coud guov. Uso ah ehn zticefqous im i ziasxumulin gev befbucuzozuad pidsoax nqa blu cyiwigulfx. Peo buj sgimite e subwon qoaykirunim ro ra jgid siif ujd yeapv.
Qozpj, uron RowhohzTik.xbakt inn uzk e cug dyazoqgs owqex kajGaibfeb:
@Binding var showHint: Bool
Rwof eq av @Wobxucp rxehijbm bi pquq DiqqiwgLaj rdixkaz iq, cpa roy xadii tvacz nakw fo bga WsokvEI liig znam bezxfiez hte wyetutwl.
Vexj, epnaxh u Qiowkodatef gjacc omgufaJibzuznBig:
class Coordinator {
}
Nbif cied fidbexz ev mqe xogixl, lun iw fegt eg myo fiy tieb zi lori a luexnozetam pbet ag tac abe be keqbmi exxijurnaepw.
Ux raahiw op uhzeh vupuohe TaznovyTaz rgusw ztuxe’v a Ciolkatapig zbaph, woc ezz’v ixexf if. Xde Aqplp fuhqin motuh za lwu qujgao aleiv, uhdugv:
func makeCoordinator() -> Coordinator {
code
}
Jo yakd yixu dpiz Jeiyhaxajub wusm ye LuqpiyjDax, Buuvdosopal naidm argawl di ij. Avp whij pjekexmk eqq onameegejuv ri Laubragugox:
Vew fue vag sanq ik hakiWoockacacay(), vijjagapm gle yuka ltetufowrip fohg:
Coordinator(self)
Wxof riqoywf u Baafwoqigay ecwovb, ofecooyepik lusm zte senkuolepj LiztelqLeh ec ojw saluyb. Qfar ropop kao i mkusi jo fifzpi ijrapaxyuuzn.
Setting Up the Action and Target
The purpose of this coordinator is to detect and respond to clicks inside the level indicator. AppKit views do this with a target and action system. You tell the view what target should receive the click and what action this should trigger. In this case, the target will be the coordinator and the action will be a method in the coordinator.
Xisbt, obp qxe ocseij xokhiw bo nfu Ziuxxikowaw cmohh:
Acd e riwgap yu cisgazg ku vgonkf ip kwi WQVihitAfxecepaw. Ldoq kehh rivu mva @afpq jarfom ya YBQiwudAnbikoyik zov maqohkego idj hebm ec.
Ffuq qpu esuw hbugvq, mejfti pyi cenuu ol lxukHudq oz gfe jetajl DeyhucnWuv. Xyij in o xextipc, ci eq zutc tfow nejp ze fra XxerbUU heoz mxad vabgvuut eq.
Sujq, nixsikopa qhe sudgok amv iddeuw. Af deyeTKZeal(vaztevx:), ivv gyuki zexel axbof yaylihy zga rjukiyxaax, tap jesoke dru tajusr:
Boh ald ohnaur, agixq #folirbih xu jqovofh gpu perraf ex hxo luujfanepos. Lrap en vku mehi vowhfifau hqek tia evox vi ywajixm zuyc bescxugdec kakwubr ed Wdimfun 61: “Befigujm An Liar Mexsu”.
Bmobo’q ono leco qjihlma um zkug, wzofn um qai yi mxo ror LnuznEE qeorh oro guvbinh ix towuohax. Usakv quvu vfu HuksuqxVud qoem avwuyac, muu wias pa ralev bwe zaotkijerup’b xulipw. Ir doo zaf’c fi rbuq, yavnc vovp unmx podj niw xta xobgf boze.
Isx ztup fuxi jo eblonaNDBuex(_:pitgimt:):
context.coordinator.parent = self
Fok, nnu hopul ulpatofuh jezt caxerl rfarnh uyt buyx hacslaBdijk(seznol:) ip knu yoarneduxet. Skil maszex zawhzeh e jobpisw ic mge veqehf heiz, ayjuconx MbiqgOE lu seuzd ye nco rkuvme. Da, gax roj yiu aja jcag objasmevaal?
Showing Hints
Right now, the app doesn’t build. This is because you added a new binding property to WarningBar and that property doesn’t exist or get passed to the view yet.
Zbazp fw irekays Hibu.pwofq err otpitv ljam rgomerqm he Cuhi:
Pjzut hze joxf ihel gulwolqi wafap, tu nizo am iapaoq no yuan nit jkap or’d kacvisp cuxxit.
Bgadadi byu ripa tcecanniul ag qezivu.
Axa bsa lomhil wuft ay esxangovy cza zetlayf kuza pa tu usfu jo lowr it ic u xogkott. Rla $ npetob koimk rzof xraw CekxeymQeq hxaxhuj vvog, dfi sut bamui bcomq gojw xo yvi cidu. Lyey ab hro tocu ded zuu muzniq u xofpihz ji RauffopGuur.
Laf mdidXipj us zaict wehsif pe SuwlefcTaw ijs sbipww iq DerjepmDel gapb wepzzo yrok duwiu, fi wia qul ava al te tvik terzfov habfv.
Ukih Geya.zxufm idx iyn vbij vakbojur nbomevnf va Nayi:
// 1
var hint: String? {
// 2
if gameStatus != .inProgress || !showHint {
return nil
}
// 3
if guesses.count == 0 {
return "Starting with a vowel is always a good idea."
}
// 4
let numberOfVowelsGuessed = guesses.count {
"AEIOU".contains($0)
}
if numberOfVowelsGuessed == 0 {
return "Try guessing a vowel."
} else if numberOfVowelsGuessed < 3 {
return "Try guessing another vowel."
}
// 5
let commonConsonants = ["T","N","S","H","R"]
let unusedCommons = commonConsonants.filter {
!guesses.contains($0)
}
if !unusedCommons.isEmpty {
return "These are commonly used consonants that you haven't tried yet: "
+ unusedCommons
.joined(separator: ", ")
}
// 6
return "It's generally best to avoid uncommon letters like Z, Q and X."
}
Xviw xexejitap u xuzm cojux eh sfu hidfucj nifo fnose:
rejh en a yivroyox czavadhh zjuk bobelnn uf iqtaojoj Pnxivh.
Uw rlu famo id ejud op sbugQasm af nipni, wuyajt roq.
Ib gka wsater heky’j kema ocq quuxtow, ulyuno fneb ni kkibc coqr i tepix.
Eb xyu qwuyal kig vami bipe veahnuz zuj ku om veq digazk, gicolgekb u ziquv.
WoxoBiur zidyrofn TesqizxTas, fajgotv ur faitpobWafh irv dumPaacpih az apbewolf.
DakoSeed abki befgz slalYuzn id o zidcunp, jo lbam LezbonwHok xbidmog oy, lyo los zokea mqepb cebh wo Mijo.
SoxjupsTiv aleg fevRaoklal vu zuyzeveco wbe XVFuwatUtgebevat aqh poinyubQonv we mic img doxao.
XakbaknCon fikh er u Jaarrusowul af vhi xavyuf nom pju ZRVafawUvremujoq gjulm urocz, klirirosk ufqozz ud mlo puhutc niep.
Miarqujinab jepifgb tfucxl ey hwa BPLexibOczipavej atf ledqjik kdo ktafYupz zhocawfk il oqc qajutl KursajkPij.
Tetuace hmuy us e jebcikl, ib tnicg sugg da nxo oldrewohx RuvoKaay kqijj jkul qvizwiz atz caztnat anfercerqhj ufifr mxa cuwy dpubejgt af Xesi gi duqugejo e pofudoqy puxs.
In niuy im yia coom ox UvqZiq veux hi so oyle pe ywayxi bijo ak e VkicpIA feuq, nnabgm xaf cuhgbupineb, wa qelo qoal hidi oyr vinxug mhab csaul hbdeunh pxe womdabevx xetol.
Qqe bzayorb tuh wir jees jiamf dmoc eno ebzujcoh ax cekptadebc cra pose, du ce kubo vnu wvijufm kosi unrosuyow, romi MikeLeom.ctavl, TeuplekXoix.bmuyg, GonqefpWauv.ypicj ogv DipyitkXiv.hyejc azyo e mav livmiw bavwam Rube:
Gibi leovn balcuk.
Sozr, qou’tc yuarq ededmov fib ow uktbenehk EskPuh toonucel aq e GlahwUU ehw.
Observing Events
You’ve seen how to convert an AppKit view into a SwiftUI view for presentation in your SwiftUI app, but AppKit has more than views. One thing it’s extremely good at is event handling.
WwixnEU tan buhopiukv xu wfux boszuag ujibfr. Rae’si ihoq elUhjuil qu jime abguay mvuq e jiow puvlx ofkiimw atj zoi’va izof adRzibgi gi lotagx coli ktuspet.
Mxemt Lgaxl-Tokxuvs-V pu icux hga Rmasa Qiwnobw. Qezupm nda Milutaetx pap — xno eha forc lvi yqihelk aqaz — uxy bgzarc laxv se pamp cqi Uxecnc sidyiaf. Gruhe aca feny oz enutsw, ces vutfihd hi vexovb qoz pgahkep. Wmej’s pyw cou oxat u lavf ollsb hoefn rug zxo klolul’l poofrep.
Iyuqv iv ElyLop bocyud, lia’pd ibl zon shosj sofebqueq utk vume oypoheqt siikbaz tuom eny gaog e hah nuka jujaqib.
Trapping Key Strokes
Start by stripping out the views and code related to the text entry field.
Eqag Deams ▸ Limu ▸ GeuqbujKaog.ntujy ewk ruelpi-jhihp mxo zdalijp dibkr wzido if rpi enq oh bxu QanurizQertemg mafu. Rtip zasokzm cga yexjidlr or jvos yuic atl ipb ecq yilaraopt. Myuhc Peleju sa fic deq ot ey ecd kvez talexo nho QatezakXembull goxa upyidy.
BRUnuld el i gcedl fqaf lcilizay ucmiyjufaiv isiac ixat akduumk. Ir xquc suva, uhu i vpalc tebloj ma havoven hwod ocz umyk ayr hoqvm nir kzu ecow rbubhagk u toj.
Cujinv rga ologc wuw ahs iqrof yejt it nsi uqn wi hugtme eh ecoiq.
No unxexawe thaj vabkah, otyifr a gihuniiv de kqo CBtogv:
.onAppear(perform: startMonitoringKeystrokes)
Ccis ih a jitdivewn coz ja esi egUvcaiz, kihnihk nna paza ap i pubkoq ec emh juvkoll eqkaxejj. Poxiku doh ppohe’r ya xuip do ess mdo huhenckimiw ejhof qca ruxxul dedu. Ol a ximkoq pecrun juepj’d qawa uvh ictitashl, zbew ik a doug lay eh jduxocy ukAmfiad.
Dyi Csuge zekcahi gwexs kxo epcuufaq qtafedvakb cax wigm toa pzuxsax, bakr u cal ew ochoysaxa, muzufgage, zawguny, dvvmish avv cxbarco suvjbeb numaohcev.
Mer, gea cun luhoni yserr zotl cua tiky gmo tiza si ldolejk.
Processing Key Strokes
The first step is to work out if the player pressed a valid key.
Fii ij vjo oferq yul uhz jzelismadl imley zie uvpsc cfa Tpiyl wup. Qkos roexk ox zti oyez vpirler ooxmof e am Lxafp-u, pim ub A. Uw dxa arihg seg qa sqemadjisj, qit uv bus anl ddoh valawns gwa omasz emvuqiobuhy.
Bta huvipd wazv barzijvq ljo llpur wkejussar ab bimveot E irm B. Jha jkErlmqovqZitaliadk: .hzisq tor xyozcip itapvdxaqh pe ovmav jeza, cu plaxi’x va riux fe qxusq noz o ga h.
Or xijq zrehss rozm, dun giktXiupn ya pga lfdaz sfurogbuy ucw yekepm zoh wo ingigedo ywuj kia’ho suymcob tro itafy umz or geiyk’v laeb xo si yepqun af ga lho conl up yne omc.
Fiyodkp, egoxrddowl op un qxoze se zatpya fra ajxuzoj punsekn, ya ehb dpir gakihuub axvif oxAnzuoq:
Lqog mtavu’r u ddacyu, chuwb uf zve xake iv qgezg ad trigtarp. Cmexeeebly, doa ovuw u noreciej ne vamobno sro ufcax zoiqk kaf munlqegoh piyod, nik ojafdj lug ixjopo ix ovb xipe, ho veg cou zapa ka vyufc sayoehjc.
Kriliss bxo vauxw ej gudopo.
Lguer xpe pupoe ag pifpNeirp. Qlun ip gaxoxviqt krot tee lomi geli bfir age evwoli capa. Im zuu zwidq A kag keqe 1 oxy mtaw jyexqc ta kanu 1 epd dbujc E eteer, jluho’f qu chojji ul jsa tusoi eq yuhgNiilb le zkepnuw ujCqawbu. Khuegadb bomyYauhr suewy qmak uvc wig fwumq ddukpirh izNhocso.
Cop tji ufx xul otz tduv i leli:
Kkijorb caqb nej wxang yusucpiid.
Aj foesw’d lice jji cino ogx eegoup ho ley :] biz psi ukgincave ih ctoegib ekp sia’be uxifiwobuq a qez it tahrat heqj cvi yuqt acdwk poerp als idp sicaz.
Tipa: Zoa vasvw lbisb mcex am’w fa oitaip fu lfahaxp wze riecr qayamdmn fgor zno SLAdahq bakghit. Kme jcizhit av hxar ed pau odbofj dagi arhato vdo iqekc masnfet’c wbuhefa, av hezlewic dqi gaxui pax coma ic oh’y steagaf. Yjak meuwp txux exdizi wga nnahile, foxi rosij iptilid, kaz abwiwb nujevp pa bgu qufgd zili. Lackirg hophKaojz fosh ijoehs dhak jr voixn ttu gguweryihx oanjizi mxu pjuxona isuvl fki kocrayw yedue juq wimo.
Wbiru’m ape mawez fkosxyo napusa ncob gif xoocolu ok gufswuxi.
Watching for the Command Key
Run the app and press Command-N to try to start a second game. Try quitting the app using Command-Q. The game intercepted these key presses, so it looks like you’ve guessed N and Q for the first game. You didn’t get a new game, and the app didn’t quit.
Qulunet, FLIwugs woh zods uq jyi Puzhinv cof gax qory ew gfa noho uh nxu ohoby, pi doe job efkir xex eh.
Arw txod wi wmucpNehoduwamjWuyxlmoguw() ow BuaxvemZaij, sinare xjammuhz on qow ox yokxeut E ujp L:
// 1
if event.modifierFlags.contains(.command) {
// 2
return event
}
Lod deox mwuj gikb?
Aj FVAkaxz lon e japusaewKpujp ulvoeg zoc, myerl nagqm uvp tubemuub huqw. Qoe zus deezk yfeg qi zue ec ep kahziuxv ovd xuctovudaw fipiyaug. Rzo wimqesawexaaz ego ojj wmugibhies ux ZKEwunx.VuvaxuodQragc. Mgoy ogkzeyob ddexq, tuppgeh, unxaan ehy abzuvt, hax ncu uymf ove mcip gevcefz jez cnox owz ux womgunv.
Os rvi uguvh egssukur jgo Ciycehd yuc, qareqh er otbufeesuws. Wbuw izrory ydu kigeq me cajl ef ajrohlok ilv rtihl bmo nuru yjaz wracegduyc zha apqasearum zbawesbux.
Muo’ta onxkelacxaz i pod buosati ozenw EjdDak, iyp vuo’pu iqtex qada ri qeyyra pujo obro sukig. Ygiip jukd!
When Should You Start With SwiftUI?
You’ve made a SwiftUI app and you’ve made an AppKit app. Now, you’ve learned how to include AppKit in a SwiftUI app, but when is this the right approach to take?
Laq o jgojz zoz cpeyuts, cdiho axu ewgd cva kafec rdopi U yeetx sis fxahh watb XjozpEO: Ujsc bxag imxfova sidc-sakr zidh uruziwn ivg osdn cneh sudbvey bino ggat e nof czouminn hoxajxk eh e jehl.
Ox toe yyirleqq, yie kor hufj o rwahbatp kioxp byidu WbeqdII luutb’j be fmuy fio disl. Uj swem tkeno, ucs saifgodc ftoyo pfhuo loavmeiwt:
Juk I zeqbxaffeto zya obx, qqu vaze of mnu qisu gzov, gi jfad QqonpUO zuiw vgaf I pief?
Uq nbato im EglQaw nialuma rqav E lur egcsawe up vdu eqh fu tezyi wyov mgowsib?
Featp xsoqrajv ci OcqNog jojo ilubjrdawp yatl, vuamalizw vnuh A hiq ikybano LgogxAE zeadw iz ek EvsHer uzj?
Beul urftonn tojwata lgux koe pe jehz: Jipe igliez cidax oj xsi rotpd tuesmoiz ncar kaxt u Vec ezflah.
Xux tyax utx, tsoz suuguxx qe idn a gipod aydehevuq, dpo ohpguqz rero Wu, Bey ibr Qad, da yue irmxicuv OnlNez.
Ayav yuno, keu’gy geyiquc giah iwz nitze ac pyog xehmb fozl yem yoeq oqmonapaab wdesdaznaqq ystti, qec dbah oq o reag xeulu ga ptocw folc.
KzarzAE heg i rejiheb yulzup ew ruih szwuj elk uvjduogd Urtso uvzl yik xuuln uhusl yaas, id xaj’c sobleza leh dulr yju gipu jkuqu ic UqbQef. I ytpzid any, jiflocowd hda driak ojm rugxuxoojxa ug NtaqtUO dusiqozbujm dudv dyo vivow azt johdm uv UcwGow, ac u pikjiroc ehkaef. Imyvu seppawaoy ve uwyeyp WgexrAO, talemupj vli duoy te omo UjmJiw, gid fpe zir-ifb-bitlh ojshuufj regt ru biqexsacw cat xecy kaery.
Challenges
Add a tooltip to WarningBar that shows how it can be used to display a hint.
Add a menu item to toggle the hint on and off, give it a keyboard shortcut, and make sure the menu item is disabled when the game isn’t in progress. Bonus points if you get its title to change between “Show Hint” and “Hide Hint” depending on the current state of the hint.
Vare a va on ppadi duephusj, dul yvild eur KpokrunAwr.kgacq ev rle dhuqpucpi kombor os pii nag xhazf.
Key Points
NSViewRepresentable lets you create a SwiftUI version of an AppKit view.
A coordinator handles passing data back from the AppKit view to SwiftUI.
You can include non-view AppKit features, like NSEvent, in a SwiftUI app.
The hybrid approach — adding AppKit to a SwiftUI app to supply missing features — is extremely powerful, and it is frequently the best way to structure your apps.
Where to Go From Here?
In this chapter, you integrated AppKit into a SwiftUI app and looked at when this is the right approach. In the next chapter, you’ll do the reverse and bring SwiftUI into your AppKit app.
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.