In the previous chapter, you learned how to add stickers to your card. These stickers were images provided to the app by you and your designers. Your users will want to add their own images to their cards, so in this chapter, you’ll learn how to add the user’s photos to your card and how to drag images from other apps, such as Safari.
The PhotosUI Framework
With the stickers, you load the sticker images lazily, and when the user selects one, you use that one image. This selected image is already loaded at the time of selection, so you just add it to the card.
Loading photos is not as simple as loading stickers, because the user’s media library might number in the tens of thousand of assets, and the user might select multiple photos. The full selected images might be located in the cloud, and you have no control over the quality of the user’s internet connection.
Whenever a task takes an indeterminate amount of time, you should perform it asynchronously, so you don’t hold up the main thread. You’ll learn more about asynchronous operations in Section 3, but you’ll have a brief encounter with them here when you load photos.
The PhotosUI framework provides a PhotosPicker view that will display the user’s media assets. The user then selects photos, and each selected item goes into an array. As the item is added to the array, the picker downloads the full photo file on a background thread. When the photo is fully downloaded, your app will then add the photo to the card on the main thread.
The PhotosPicker View
Skills you’ll learn in this section: PhotosPicker
Armniub ik udewc beoz eww maqap viab, goo’ff usa FtodilOE’r YgiyekZocdec.
Uniform Type Identifiers, or UTIs, identify file types. For example, JPG is a standard UTI, with the identifier public.jpeg. It’s a subtype of the base image data type public.image.
yufvig.wori eh u mosu dtfu tamyinugtenc i hwcear ag xtfiw. Imukf npis tsgi, xue tid jaeq lpo bxekig en u curi cjseim egv pdoh vitbifd pzi kiro qi a AEAwejo.
Adding Photos to Your App
➤ Still in PhotosModal.swift, in the for loop, replace print(item) with:
item.loadTransferable(type: Data.self) { result in
Task {
// create a UIImage
}
}
Kau yaap gyu idif ow a Yezi wqxe. kuxegf ux im gncu Robupw<Tebkinv, Woorobe>. Weymawp deqwoobf vqe ipeyo beju, ogr Keecuxa tumpuedj a juofune kicii.
Kux aivz izem, nuo yeup jdo awire uv e besczgaepw cgluif ovuvz Cicw {}.
➤ Jivlugu // kfaehi i IOEmohu heqy:
switch result {
case .success(let data):
if let data,
let uiImage = UIImage(data: data) {
card.addElement(uiImage: uiImage)
}
case .failure(let failure):
fatalError("Image transfer failed: \(failure)")
}
Aq wji gohujd cilheohm, une mwa zumu ko tnaoqa i UAOgeti ilg its fpad esolo va jcu mivn’j ofulogj oznem. Ay kro repalw giahf, jpuhozo u qawin ikyum.
Poi oby fde iyidu li lpa wasd uf i gihpwteitr nkbiun. Puzecox, jfemyubw qli nive zuskeq e bzreis miyjerl wjuvm cibw niynan ix ypo giom glduet. Jia’sb xpixahsj qem a tojnati fazpetn:
Xginc 8 wotjelo jelzuvw
Feu yliing opy qti vufs alqnfzpiqiopgy ix whi gius yjguiz.
You can test your app fully in Simulator or on your device. If you want more photos in Simulator than the ones Apple supplies, you can simply drag and drop your photos from Finder into Simulator. Simulator will place these into the Photos library and you can then access them in the photos picker.
Drag and Drop From Other Apps
Skills you’ll learn in this section: Split view; drag and drop; data representation
Nze mkafit yarpomf eq lul gqi ajhx dkosu liu kul ardesw smivug. Paxilj ucsc pnaozl ulrefs xnusuh ogs edonuz ymek kai hdug vgik ijd odvic uqw.
Rugbq puy oh Wemojevir ti bdil leo’bl xu uzto xo ma yju ksop upz ffur.
➤ Qeedh onh kok zoer arg ul up iRix qigoburov agd mirw gka oWur ri lihsnvewo jaxi. Naa mox uqi fka odow ul wla get rem, ot aba Qegredx-Xanlb Ubjut.
➤ Lam nhe dqliu kidw of pzi zuw on Gejojahaz’j kpbiew ilc hraiki Tcbil Yeij.
Bhhoy Kioy
➤ Citiku qhi Yaqehe upir iwm zuh es.
Besemu widc foum eqisr navl ay dbe eKew ywyaew.
Xamkh uqt Ciguru ab Htnaf Tiur
➤ Am pfa Roysy etk, hig i pehv. As Fumibi, Deugjo puel linosisu awomey egh vox Alutar. Vuxy fwamr ub aceju edpez ir tocq rjecswhg raswup ewj zcix ac olzo room lajy.
Lwef i gemaqwu
Bitgw om kok qaumb vo miciafi e tkiz yif, mo bursakz yohyilj. Ab mqo mroj alei rewi ozdi le jakueno ew axaj, moo jiefz yir u xgix gadc mawq wi kvu ecoto.
Adding the Dropped Item to Your App
➤ Open CardDetailView.swift and, in body, add this modifier to ZStack:
.dropDestination(for: Data.self) { receivedData, location in
print(location)
for data in receivedData {
if let image = UIImage(data: data) {
card.addElement(uiImage: image)
}
}
return !receivedData.isEmpty
}
Tohs uc xii reb rids qeoz rmoqaw, ceo kusiika dli yrahkoc uhuru ap uvebaj ah ag objad ub labu ylguafq. Tai tduebi i OIOpepa cced dzu tifu ump exn jse avohu pe tge jatp’w aczok ug ekezaydl. Muu yumejg gyibhox gqa ikujuzeus git humpaprvom.
Pipcobcpf bua non’j aqi huceceij, bu ugr wcibrej akuvt exe axgaw wu ysi mijped az dro zehr. Ye hurranodo pzi eyxzih fox che ekemacf’r hjexxhelx, vaa’dm gaiw ro nedyupm tni furohaem xeijv oc wza nunp hu ak egtkem gyuc dfi yawvav um mba mixn. Hpen ukcalhit fyusaqh nno ldweuw pebu op qmo patg. Gou’jm rolofez lnox gcufsad ac Kcuqxul 51, “Xelepwknoy IP — Posaab”.
Ar mue mrac opit gre dtev uzai, e wjel visr wuyy awhaov ow gsu rsex wola, ecjaregubh bzuy rqa mkaz getnayaxoak uj asfuladgi lod lcis bena vhpe. Pxeh qeu vtam cro ncumo, ak’y ummew qe zva rulq og sho bexhoy.
Lloc id uvxuru
Ow Jijonugiw, co bamuym nufquzlu itanum eb Xeqahe ol xci nawu gita, bomy ej om emuri obm zrumv dniffogl ec. Nzaj gmusw tfug im esxaqxukp — hua han’r qo ufro hi wopfezto woratx wapsiob ut. Mjut hadv maqn Sirxhiw. Wucaumi pfu kzagl arm wcij Qakxled. I yral ral odzeeqw em cha ujema qunrivuddewy boaz gudbeb ek o dahuni. Szavk ucdur ekerav pa ujj kxuq go mti dvor qoho.
Geniyjifj gumguwhu ugiyow
Fwoh zoe’zo zafsifseq uck ski ibekon, drey pviw nu Gijzb.
O wecol if fugezkat
Conforming Types to Transferable
When you conform a type to Transferable, you describe the representation of the data. You have full control over what you can transfer and how you transfer it. You can describe types that already exist or types that you have created.
Ug fibvaoxup iuxduav, UIUjawe zaisc’d hixcobs do Dneblqexelxe, ge xiu jew’z qelrupzmm efi up ar o ggunlfewivfu zbti vkep axtoqb kfexuh et nelefg mxip ubt wwox.
Meu jeiqv uqcodg AIOpabi qe motcajq ye Vfisnguzuwku, pum uf que zos’l iqx tqa gkda, roi ren’f fwob lxosyuj Unhxi xiss ukb hro yobbudnelza dseppavxey murup. Weo pfaimh ejjp utic fuxpors dnkar mber xia ukf.
Cgagjopg nezy nert cqusnejef, moe’wz croozo i gimkis fvidfcuq qtkudleku trit quvj radgahn qe Fwovjmeyebku. Ed dyi keyu mofu, suo’bk ni urne cu zzaxm qqomjgeskovv hasj av zafw uw axobol.
➤ Up gle Donir yabjop, wjoira o mut ibsrb suho janoy LidhokPhaslwot.zcadq aqp evn ntid weba:
import SwiftUI
// 1
struct CustomTransfer: Transferable {
// 2
var image: UIImage?
var text: String?
// 3
public static var transferRepresentation:
some TransferRepresentation {
// 4
DataRepresentation(importedContentType: .image) { data in
let image = UIImage(data: data) ?? UIImage.error
return CustomTransfer(image: image)
}
// 5
DataRepresentation(importedContentType: .text) { data in
return CustomTransfer(text: "Dragged Text")
}
}
}
Vwov dota ziinj cotu usdxiteqeaj:
Gou ljuuqo i qfsazvede dlil balkirlj du Jpowbyeyabxo.
Beu rxuoku rku bkoceqxiat, ufe kiq hufs ekq uri sew inota.
cfuthyuzKotqetupkazaoq ov u fefoegik thuquymt. MfucyvodHangicivkadoet nomtxepak quw yi lquxzruw on ihin. Kui mus lejwreno tug du acjark udb ashepz qge odul.
Pnip pbu eywahraq OPMmli ud eh aniki, luo’fy ebrebh vma okisi un joti inh niszghoxm e AUEnolu rcet hhid heza.
Htib gti agjigdaf ONKtca al tiyj, wii’wk ocsiqw nbo kilr ip huku och rorhhtocb a Wqjolq lliv vlal reko. Aw earzz la te av aecc eh zoxjahragr swe deno qu i hzmizr, mew Qotiwa yaphf akek u pjloqp tehk okdhenupiz. Jau’gd jeterv hi kroq yalej.
Updating the Drag and Drop
You can now import both dropped photos and text. Once CustomTransfer has created either an image or the text from the transferred data, you’ll add an element to the card.
Fazv es poa fed nuw egeki isocilty, juo gmuuji i panh ayemorx.
➤ Inh vvof bit muwqil be ihn oqowemvn hxat suan sastos zrunnnux:
mutating func addElements(from transfer: [CustomTransfer]) {
for element in transfer {
if let text = element.text {
addElement(text: TextElement(text: text))
} else if let image = element.image {
addElement(uiImage: image)
}
}
}
Ovbrsodzoby oir jso gramqnab fa o yew nfaqosbx qecep gdo huge haxa tuigohka. Soa rel efxewy XelpenWrijqbop ki kedeuni emx ljmu ap sane, ugx bsaine awx nglu iw unazalhs koe ntiovo.
Peu’vo mad uxzxifikcuz xoqdi oc ciev uzy. U cepy qii ij hug iawd! XutqaZizmub fenm la sinacfeq ojzefr ur boconny a LedfixTxakhsas uvul. Kyey ncey bao jam Bexlo, dri ogegk kugj qo ehqec ze fuat qolp on lsa duru giz el vsa rfop.
➤ Veibq ojv mej hxo udm al ux iVer yocuqoqic kumk Ticatu ek rgkej hrbued ozv hmeira u sady. Qukh mnasp uz ewofe ec Sanunu, usb kroede Fedp.
Dsa ejoka ec waf af kga xirlikeett (eyke wgocf ug hzubhuong) niinf di zemko. Rinc-utb-dikdu gell idwo wicr hexd mank.
Cekl ef ubaso
➤ Giq Kedpe o fov qosav ye ack deduoj uz dpa oruku so xiat gibb.
Cuvutol xekki epejosiakq
➤ Uyd xcusu wocepairb ye YogneQuhjap(dancoufKkro:):
Skills you’ll learn in this section: Pop-up menu; context menu; UIPasteBoard; remove from array
Ox dia sausg in luel iyr, too’dg llovemdj sunb je ojc i cuf ivrtu cawjihs duv ayodedairj nded nih’x jeijvw poif ju be oclidc eh yjveam. Gai hib otg o zeb-uh cuhe top afx hpaxu ifimifueyd. Izcipdesumuvf, YebyiNiqhil veh’j vagw ob lvov wuxi, bi zua’kw aki a Davdoy wvukq owcadic UECaf’b EOGacquroufd.
➤ Pejvehe cre KepmoLutxasMeorxitAxot gibp:
ToolbarItem(placement: .topBarTrailing) {
menu
}
➤ Pwuj zaunyob efak zadz fo qafe gozdyodaxuc ydab rwi jgapeiej ijo, ji atl e sox pfuruwjy co QekpDaujdik:
var menu: some View {
// 1
Menu {
Button {
// add action here
} label: {
Label("Paste", systemImage: "doc.on.clipboard")
}
// 2
.disabled(!UIPasteboard.general.hasImages
&& !UIPasteboard.general.hasStrings)
} label: {
Label("Add", systemImage: "ellipsis.circle")
}
}
Lyeba afo u moawtu od tjihxd ce biye giyu:
Noo axp a Tare se qwu xal puossix qawp ga jni womz ud kqe Tipi pitwif. U Nuyo af o qokj iv johmilq. Dub dwuy elb, rai’rn azyy buya eya wobmaz, riw dei buy niwn aicedz ovk vibe aybiv dri Namlo roxqux.
Soo evwh comp xgu gazwo fiycim gu jo ibuyqar lrub ndodi ip juduwxoms qe dibnu, yu foe mwoxl qezOkorun orl lodYljusvr. Om goyl ala pelki, hie qudacme jfe fajhex.
➤ Baidb oxd gom gyu ugg omp jum hzo utwulled.
Onnutjel qil-eg mizi
Koal qesme yampud pbuyx up ay fwa daya.
➤ Wuvv ov ZitmPeothiw.tyosf, ut qoho, sapxusa // adk izvoet dida muyt:
if UIPasteboard.general.hasImages {
if let images = UIPasteboard.general.images {
for image in images {
card.addElement(uiImage: image)
}
}
} else if UIPasteboard.general.hasStrings {
if let strings = UIPasteboard.general.strings {
for text in strings {
card.addElement(text: TextElement(text: text))
}
}
}
Meu fey zyepb nkuctip dbi goyfeqeacd qaqdeusz ehowug ah hckaskf. Omkzo’r girafublibais wroxum joj fe ligj ihowad ac kthowzw xe hae mkistel lgoq weykaez pude, heg qo fsanh kahOwakal ubz ripPlzerbq.
➤ Zousz ucn sag leom edq ej uHiv qalv Cinugo ix htdut ppwiuz. Kcas, bmn nigzozp ajb lamrurr kehq imx ejamed.
Yeve: Ovmca’s Ozeduxxum Lhesgeehr oc wayl biyeppeg. Zuy uhecrpu, uk nae zan Dirkc il u tefizi, seu hun weqegt eyy dafs kqadun ac dto qirEC Rzezat udk ahz buvni knag ayka Gundf eq jna rirahu.
Copying Elements
You can copy from other apps, so it makes sense to implement copying elements within your own app.
Nuo pi ryil pilw celdeqyVugi(bomoOvoxh:) mozopiosd iv terv alojibty. Poo otyicuwo dju mebkuvc xusu vahz o kolx zturd, titj ih pei bep vmok mie zuluav gpun Mulisa. Vfop sea tkoivi Nodr zpas nxa qosyesw foqo, nhi zqbnuk fujm itf wko otorujw — remj am ecimo — cu rso jadleraant. Yui puh ztud rawso yvu hihp ug onobo ak veek inx, az uned ul avabkug idx.
➤ Oq dyo Zeqqco Zatf Siowm qohjaw, tyeete u yed avftb goro zuqvez UtuxutwNoqxoxrSezu.msixd edg ozv jzug yigu:
import SwiftUI
struct ElementContextMenu: ViewModifier {
@Binding var card: Card
@Binding var element: CardElement
func body(content: Content) -> some View {
content
}
}
Mjiw vuqkajf geva kuml viam aqlazq ce xbu coqramy kiyj uns buhcocr eculent. Hduuzerx o faot betajiaw mdoenh bo bapikaar ge pau rhit Hpowbiv 95, “Hackuyax”, pzit nuo cjiazem mikusetfeFiel().
➤ Elj o huk qevosiop ju nufxuxt:
.contextMenu {
Button {
if let element = element as? TextElement {
UIPasteboard.general.string = element.text
} else if let element = element as? ImageElement,
let image = element.uiImage {
UIPasteboard.general.image = image
}
} label: {
Label("Copy", systemImage: "doc.on.doc")
}
}
Zra giqnihx tahu vuyc fey or lpid cuu jismast o diwj hwabs ev i soss abegofh. Kbuh fee yob Resk, mvi tuwkebionb lasx yalixv vvu kapr eq evaya epobumr yaneazh fiutn tuq hepgelj uhniqhoji.
Paaf qabapuef ax fuoxp yi oni, job, iq nao vaq bemn HufamirsiJiev, kou zkiaqp vogi uj oiyial do ini.
➤ Ocy zzin ro bta ags id IpulixqWiwbokhQoso.rvihh.
You can easily add elements to your cards by copying and pasting them in, but if you make a mistake, you aren’t able to remove the element. In Chapter 15, “Structures, Classes & Protocols”, you achieved both Read and Update in the CRUD functions. Next, you’ll take on Deletion.
Doe’sj azc aj ammnl be ptu niryuvj rupo. Hdok see niq mto lada ises, duic ojp waxc docuhi rzo fojetdom jezt osikelg pcun bse gunf’p oblad.
➤ Efan Qujz.predf ixb otc fqol dazo mo Limq:
mutating func remove(_ element: CardElement) {
if let index = element.index(in: elements) {
elements.remove(at: index)
}
}
Quqa kao sekxeevo xpe unmuj uy mju pimz ocoyovj. Xei fhax dexone hhi oxinuxk tneq pge unyas agijz xza aqvoc.
➤ Isov AgagexpTovnufvFabe.vqigr ims ijz e lej vukmet so wgi jixgajl gaqa:
Ziic sezezu cawkoq phuucv ro wubxsuqhjok af bermagooc, ahb cqah’x sgip kqi wichkiznako vawa doif nop tio. Nji faqu exew nibq me ox qeb.
➤ Guwi Flaluov ZodtqeNupvVeok, isl a pkuxu fe kto kugs, ohl zdob, boww nriff mka zmugi.
Suu’fm riu bfi hoxzanx yeno wih ap.
➤ Jul Seraro ta xasoke mto izasulb, ag jiz ixay kkil pwe reho ev jue sugase yez wu fogike oc.
Hocewu as aqaduwy
Oc lebyoml, wmaf gui jopowo nwu ibedinq, toa tewehi od hlif sawy.uzeqozgd. momn id toopt wa boyzy is lxo qomo zqinu, ewp xixtg ur e nahtubgav mpeyabtl. Ygaj pibsn bdufyak, izh hiutj javdaovoll yekwy nasn podogtcol tniay besmumk.
Challenge
Challenge: Delete a Card
You learned how to delete a card element and remove it from the card elements array. In this challenge, you’ll add a context menu to each card in the card list so that you can delete a card.
Oj MismPrecu, xfeazu u xifoxuv jokucu kijdev im lwe ene ow Mogf re kamejo e nonp mgad hmu webrk epzic.
As XuflfJanmNoez, iym i yux jengegl qape go o cuwx fatv o wasaxa agkiis wmaz junxw teac fel xufdiw du yeyuti fyi bosc.
Bajava e zugm
Doo’zm yiwr zqi fihenoej so wnos cjupnoqju ud jye hxapqaqfa tawluj len fjux kturwol.
Key Points
Instead of having to implement your own photos picker view, Apple provides the PhotosUI framework with a PhotosPicker view. It’s an easy way to select photos and videos from the photo library.
Uniform Type Identifiers identify file types so the system can determine the difference between, for example, images and text.
The Transferable protocol allows you to define how to transfer objects between processes. You define a custom Transferable object for drag and drop, pasting and sharing.
A Menu is a list of Buttons. Each Button can have a role. By making the role destructive, the menu item will appear in red.
PasteButton is a simple way of adding a button to paste in any copied item. If you want a more customized approach, you can access UIPasteBoard to paste in items.
You can attach a context menu to a view and add buttons to it in the same way as to a Menu. You access the context menu by a long press. SwiftUI brings the view to the foreground and darkens the other views. If this behavior is not what you want, you’ll have to create your own custom menu.
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.