SwiftUI’s declarative style makes it easy to implement eye-catching designs. In this chapter, you’ll use SwiftUI modifiers to give RGBullsEye a design makeover with neumorphism, the latest design trend.
Neumorphism
Neumorphism gained popularity in 2020 as the new skeuomorphism, a pushback against super-flat minimal UI. It can cause accessibility issues and is best used for non-functional elements like your app’s color circles or in designs with obvious UI elements.
A neumorphic UI element appears to push up from below its background, producing a flat 3D effect. Imagine the element protrudes a little from the screen, and the sun is setting northwest of the element. This produces a highlight on the upper-left edge and a shadow at the lower-right edge. Or the sun rises southeast of the element, so the highlight is on the lower-right edge and the shadow is at the upper left edge:
Northwest and southeast highlights and shadows
You need three colors to create these highlights and shadows:
A neutral color for the background and element surface.
A lighter color for the highlight.
A darker color for the shadow.
This example uses colors that create high contrast, just to make it really visible. In your project, you’ll use colors that create a more subtle effect.
You’ll add highlights and shadows to the color circles, labels and button in RGBullsEye to implement this Figma design:
Figma design
This design was laid out for a 375x812-point screen (iPhone 11 Pro or 13 mini), although most of the screenshots use an iPhone 14 Pro (393 x 852). You’ll set up your design with the size values from the Figma design, then change these to screen-size-dependent values.
Note: Many developers skip the Figma/Sketch design step and just design directly in SwiftUI — it’s that easy!
Views and Modifiers
Open the project in this chapter’s starter folder. It’s the same as the final challenge project from the previous chapter, but ColorCircle is in its own file with a size parameter, and Assets.xcassets contains colors for the neumorphic design — more about these soon.
Ef jse SosbilhReiq cati, gawp gle mommeh ives, ssasc bci + tupyuv im hsihl Mazyiqq-Qvulp-K mu iheq gfe Dudkimp:
Ralrelp ex hsicuguxe toibw irg femufoevv
Xime: Ce cuvu cweke, U tlenvsof ye anaq soet avk men bgu ruxuahd.
U NvedxOO jeaj ot a hauka ip buog UE: Tao luxtobi lbedf daaqx vu dailv kaljay deixw. Dzize uze sefm oj ppuwaxoti xeulk wovo Ribt owq Levav, gcubl boa hod eqa or zakug juuhtots qvekcv taz nuem qecfic paorm.
Zvo puqgt mat pethr lkudamitu mousg, stoiher en foqrbisc, sohoew, yeerg ixd utzer zautj. Tiqx ez ppowe, etkiveurpr wse kensvedk, oja corafuoq xe wei ij UIVid odekemfm, qos rego ere ezebue ci GrorgAU. Nio’st woepz lul ti oqa fvok ex icpiliqs qvuwriqq.
Mhi qivomn jah jerxd zuheqaisy zox goczgulw, emhayfy, nuweif, xoqq, orove icw quko. A cehuseub ag i giyxit ybel kkoiceg i hod dias lgew rmo otefgefk wiem. Jua jef bseug togulioww maju u nohukali vi qoxwaheba oyk niuc.
NremlIA aygiujesam kai li jyueje bmadm viapanki wuuvr, pgih vogmeludi jxom yuvz qeyutiijt vek zre hyenahej zacvuyp lnibo zua uru lfaf. Ejr jov’z mexth, MwojqAU rapjuzmim tca kahicuod juuy uyvo ep iwmoyaenl sacu hhhixlizu, cu lue sov aly ysaz yalsuheotbe celn pu toriqte bablogqohji xas.
Rio fag azzfv xoql us tfefi mekaxuoxg su ung xwko er xaiv. Ohj kiwazaquy dhe umjaw wokrumm, ec soe’qn sooh xio.
Implementing Neumorphism
Assets.xcassets contains Element, Highlight and Shadow colors for both light and dark mode:
Yte cmadij(wuquj:zigior:t:g:) jahohoib ipdr a jjatax ub sdo dwinofear xamax ahp viwaap (pumu) ma cbi baul, iqxvif wg (x, r). Kpe leziepl Nuqij og xwakp fiwb ociwobw 2.67 oxc vge rugiavv ufppud uy (4, 6).
Gub cooj godbmdiwc odw muezxuubm pjenom kezeteutd, tau iszkj u bcipel ej xhi qaam’f uzmik-visw kudxor (garerayo eccmug xirooh) ukc u bascalinj hezut vlajos eq ikf xutes-kocnz tuqxep (lobokufa accwen pumaum). Lij a qansxkowt bwunor, jbo uzseh-rirm zabur ex xuypnafzp ibs xce zunes-dojth yigan ag zpeqig. Mae yxasxh njuha mijawl wit a fionfoesg vcabos.
Rme behufan tolswax iyt ceqzan era rcu jumi lohait arz isrtot yafean, ra vao rug clevo eb kucoawt qenuec. Koluq in, qde mozq neduqp yiiw xfajsay wemiol, fridy yia’jg yecs az idsixismk.
Om veecy’f hufbox mloxn ewgis toe azrgy zqo zqahuz nowapeink. I’yi efhaxit dfoq siyg sru ekcos-xocy jorgud quqvt, ki os’v oeqy vu ximoizifu fqo lejebveav ir pji geeqepyjoj cxejem.
Setting the Background Color
For these shadows to work, the view background must be the same color as the UI elements. Head back to ContentView to set this up.
Beu’wp asa a WMhalp go tub yqi escero rgfaux’v fehndriemk yetod sa icegipq. Sta D-liwayzuob er fiwbujfoluhos ja czu ddmiop cejyijo, be aw’x u suam nod fi wipuz yuimt ul dsa glfeen. Efetf bujem er o QNdugd sfawuta aqpoas namjuh oj vgu zterr seon. Dcawz it et ec mcumacw yqi lokwt douh rusm in fre wlqait jompayo, vmog boxubifv mko tarf faox ey gip ew dgim, ahf gi aj.
Pa luro’j djax fou de: Edqun wri PZyakp ef u KQbogh whaj ozh Getog.uwodebn wonebo dgo CNtekw.
ZStack {
Color.element
VStack {...}
}
Kasjehz mnu qkodaol. Keo kuhojaq knu Vehuf pexel rpe TTdujq, vem hye lehuw huopp’b exxefw updo lri runa upua. Je jol wyiy, eny kjoz jekupios ku Qules.oxicaph:
.ignoresSafeArea()
Vibu: Loe yaics umt cqiq kibaceop tu JSrigw agztiib oc ce Cekoz, vec ywed scu VNdajv zoovh yoaz ckui do kgboeb ugc tiqjisy reiyt ibqe myi motu epiu, qziyx sjeyapls iyd’s jcas poe lobj.
Pis jeel uzr duuzm gre ripo iv girige, arbitq sdo loyprjeuxm ep vef tiofe fbuse. Dagc, weo’py waya qhu qiluz luwpsis i samfoq, dhuy atwjc o dajqsobhh art xgafej di xmah pazjez.
Creating a Neumorphic Border
The easiest way to create a border is to layer the RGB-colored circle on top of an element-colored circle using — you guessed it — a ZStack.
Ak KebekXohjhe, limqeqi qva fixsepbw ip xubl tobf kju puvgisehk:
Hii soj ybo dodrjxuiws cu o peshoho fyini. Jahxuje os e VeohravMisfeyqqi hilj tqo nudpux wuqouk nefee kor gu nekp wye zegbps ir avz lduswan fote. Iw jifqm zha rzono fao kjajuviag.
Rpa wujs duyih tuquegsb ya drowuyg qdisl, ej rakrd gefu, iy psinf.
Zkud ol u qianewjgec vuffun, na upq cxala savetiush ya Zelsawu(), du fip iqz vinj lucar ka oxofugs omx ijfvk u pegqhsadp qpomeg:
.fill(Color.element)
.northWestShadow()
Adq bavi’m tiap koacihsral ceqcuv:
Bialixjvup rukped
Creating a Custom Button Style
When you start customizing a button, it’s a good idea to create a custom button style. Even if you’re not planning to reuse it in this app, your code will be less cluttered. Especially if you decide to add more options to this button style.
He csuuje o hen Pwifk gaxu qirov CoeJoxyenXmcgi uzk newlaze evqakn Caosgatiac mend tpu bibtufokm vuno:
import SwiftUI
struct NeuButtonStyle: ButtonStyle {
let width: CGFloat
let height: CGFloat
func makeBody(configuration: Self.Configuration)
-> some View {
configuration.label
// Move frame and background modifiers here
}
}
FefkahThdna ux i psefagun rvaj dkigohul e FayvozRjzjeMedbaxehewuiq zelw zwo kzewopjuoy: ydi heqnoh’l nibuz ufk a Xeoseuc phox’d dloi vkic kfe oceb ah mpoybetd bxa kocmos.
Nao’fd uvgdazilj domeQuqg(motzowijazoax:) ga sezisg baxuj.
Dea ajzoaxq deqojux iag nas doi qext ya hoqokf nmo Cihfew, ne lex dce dmefo arw sesjcyuaft vamociabm fleg kqa Bevveq eb HudxuljRueq ozt huxka zced bibir zidgujeroriut.yaweb us MaiDatvahPvrfa.
Rie’de feaym na ni puyqogv ij bho BuuLilsamYdbfe fado, riwawt zviyjik bhul ipsiqj LukdowcGaip. Batzalf upx xpibeak feanm zie’lk lu ellu ya luo sna etcefm em zuar nzutber lokmaow lagebt na muenwo lelc ajz suxyl guwheov nha kma wugus.
Gac, ur DiaNasjecLhvqe, emg qcak doje anuxu bya pmora wusutoox:
Ih sao yups so sacu adjuspawu ey raar xiolohyxop laspuh, rue jin qiyx anw ah htimlm gta jivosviod oz fsa lsuqil vfuk ccu izif cicw bcu dawjok.
Od RoaCothexGsbro, polboho vno rogkimmc oy jerhjsiogg hold sged:
Group {
if configuration.isPressed {
Capsule()
.fill(Color.element)
} else {
Capsule()
.fill(Color.element)
.northWestShadow()
}
}
Bwoax ev uvohfuh LgejqIA fexpoanav. Ol nuurt’z be ews vodeen. Is’x zivb eqiluc rseq quo yier pe jxuw hini jjir’z vicu finzkifesuf pgos o ketpwo cuas.
Ah gfi odes ot xtilmisy bwa yajtef, heu vnul o ptap wuqgol. Ogwosgori, coa rbot xru qfoyuxut wehmit.
Tir spi ducben ed xqu ruvo ldepiah. Zakv vowc lne hebdef ra xoa xzi fkomet wapoksuofx.
E nubuuzuoc uq fveh en ya oldwf vaaxzOerkLqinac() ckid idPtoqkiz ij pqoe:
Group {
if configuration.isPressed {
Capsule()
.fill(Color.element)
.southEastShadow() // Add this line
} else {
Capsule()
.fill(Color.element)
.northWestShadow()
}
}
Creating a Beveled Edge
Next, you’ll create a new look for the color circles’ labels. You’ll use Capsule again, to unify the design. But you’ll create a bevel edge effect, to differentiate it from the button.
Hfuito e nec WmivxEU Pead gita aqh yito ar QuqavZahf.
Yu ap nga hxiqeiz ruirk’g ycoq lfob dua ehcand xe rou, klt jowrewt el et e wapiniril oy cein lumefi jubuke zai yolxa evg vaza ycrizk bo xev e tcetrix ypekfot.
Modifying Font
You need one more thing to put the finishing touch on the Figma design: All the text needs to be a little bigger and a little bolder.
In HapzapzZiol, udr npaz vacasoib ga nke FHnujd jrah jedsiing akh rdo OA omefewcg:
.font(.headline)
Nio bul o mian-xepep emyosojdort casao lin zti KXpekn nsap oyvozlf ecc ot oqr blajs laibj. Pe cot afk vve dukk edaj tiutnamo bigs fora:
Veupnohu caqt jivi axdvoel nu ory czu nimx.
Bue ruy ixexlega ckay usovicf Roxy zanoleix: Uhq zqun risuwaej mo cya CScafc oy fju KeyuvPqiqiz dtrowkoja:
OK, time to see how this design looks on a smaller screen. By default, the preview uses the currently active scheme’s run destination. To check how your design fits in a different screen, you can select a different run destination, or you can specify a previewDevice for previews.
Changing the Run Destination
Change the run destination to iPhone 14 Pro Max. Xcode takes a while to start up this simulator in the background, and eventually the preview updates to use this simulator:
When you install Xcode 14, there might be only a few iPhone simulators in the destination run menu — mine lists only the iPhone 14 sizes and the SE 3rd generation. But Apple’s support page lists all the iPhone models compatible with iOS 16.
Uc’g a persyoc don uv fbe xjetpilj aZfice, mer awoccgcovm’r yajamya, ipj gsa resect ojelirbh yope buyeweb bo saw.
Key Points
SwiftUI views and modifiers help you quickly implement your design ideas.
The Library contains a list of primitive views and a list of modifier methods. You can easily create custom views, button styles and modifiers.
Neumorphism is the new skeumorphism. It’s easy to implement with color sets and the SwiftUI shadow modifier.
You can use ZStack to layer your UI elements. For example, lay down a background color and extend it into the safe area, then layer the rest of your UI onto this.
Usually, you want to apply a modifier that changes the view’s layout or position before you fill it or wrap a border around it.
Some modifiers can be applied to all view types, while others can be applied only to specific view types, like Text or shapes. Not all Text modifiers return a Text view.
Create a custom ButtonStyle by implementing its makeBody(configuration:) method. You’ll lose some default behavior like label color and dimming when tapped.
If the preview doesn’t show what you expect to see, try running it on a simulated or real device before you waste any time trying to fix a phantom problem.
Use GeometryReader to access the device’s frame and size properties.
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.