LAST CHANCE: Save 50% on all my Swift books and bundles! >>

Upgrading our design

Paul Hudson    @twostraws   

At this point we’ve built the app and it works well, but with all the SwiftUI skills you’ve learned so far we can actually take what we’ve built and re-skin it – produce a different UI for the project we’ve currently built. This won’t affect the logic at all; we’re just trying out some different UI to see what you can do with your current knowledge.

Experimenting with designs like this is a lot of fun, but I do want to add one word of caution: at the very least, make sure you run your code on all sizes of iOS device, from the tiny iPhone SE up to an iPhone 15 Pro Max. Finding something that works well on that wide range of screen sizes takes some thinking!

Let’s start off with the blue-black gradient we have behind our flags. It was okay to get us going, but now I want to try something a little fancier: a radial gradient with custom stops.

Previously I showed you how we can use very precise gradient stop locations to adjust the way our gradient is drawn. Well, if we create two stops that are identical to each other then the gradient goes away entirely – the color just switches from one to the other directly. Let’s try it out with our current design:

RadialGradient(stops: [
    .init(color: .blue, location: 0.3),
    .init(color: .red, location: 0.3),
], center: .top, startRadius: 200, endRadius: 700)

That’s an interesting effect, I think – like we have a blue circle overlaid on top of a red background. That said, it’s also ugly: those red and blue colors together are much too bright.

So, we can send in toned-down versions of those same colors to get something looking more harmonious – shades that are much more common in flags:

RadialGradient(stops: [
    .init(color: Color(red: 0.1, green: 0.2, blue: 0.45), location: 0.3),
    .init(color: Color(red: 0.76, green: 0.15, blue: 0.26), location: 0.3),
], center: .top, startRadius: 200, endRadius: 400)

Next, right now we have a VStack with spacing of 30 to place the question area and the flags, but I’d like to reduce that down to 15:

VStack(spacing: 15) {

Why? Well, because we’re going to make that whole area into a visual element in our UI, making it a colored rectangle with rounded edges so that part of the game stands out on the screen.

To do that, add these modifiers to the end of the same VStack:

.frame(maxWidth: .infinity)
.padding(.vertical, 20)
.clipShape(.rect(cornerRadius: 20))

That lets it resize to take up all the horizontal space it needs, adds a little vertical padding, applies a background material so that it stands out from the red-blue gradient the background, and finally clips the whole thing into the shape of a rounded rectangle.

I think that’s already looking a lot better, but let’s keep pressing on!

Our next step is to add a title before our main box, and a score placeholder after. This means another VStack around what we have so far, because the existing VStack(spacing: 15) we have is where we apply the material effect.

So, wrap your current VStack in a new one with a title at the top, like this:

VStack {
    Text("Guess the Flag")

    // current VStack(spacing: 15) code

Tip: Asking for bold fonts is so common there’s actually a small shortcut: .font(.largeTitle.bold()).

That adds a new title at the top, but we can also slot in a score label at the bottom of that new VStack, like this:

Text("Score: ???")

Both the “Guess the Flag” title and score label look great with white text, but the text inside our box doesn’t – we made it white because it was sitting on top of a dark background originally, but now it’s really hard to read.

To fix this, we can delete the foregroundStyle() modifier for Text(countries[correctAnswer]) so that it defaults to using the primary color for the system – black in light mode, and white in dark mode.

As for the white “Tap the flag of”, we can have that use the iOS vibrancy effect to let a little of the background color shine through. Change its foregroundStyle() modifier to this:


At this point our UI more or less works, but I think it’s a little too squished up – if you’re on a larger device you’ll see the content all sits in the center of the screen with lots of space above and below, and the white box in the middle runs right to the edges of the screen.

To fix this we’re going to do two things: add a little padding to our outermost VStack, then add some Spacer() views to force the UI elements apart. On larger devices these spacers will split up the available space between them, but on small devices they’ll practically disappear – it’s a great way to make our UI work well on all screen sizes.

There are four spacers I’d like you to add:

  • One directly before the “Guess the Flag” title.
  • Two (yes, two) directly before the “Score: ???” text.
  • And one directly after the “Score: ???” text.

Remember, when you have multiple spacers like this they will automatically divide the available space equally – having two spacers together will make them take up twice as much space as a single spacer.

And now all that remains is to add a little padding around the outermost VStack, with this:


And that’s our refreshed design complete! Having all those spacers means that on small devices such as the iPod touch, while also scaling up smoothly to look good even on Pro Max iPhones.

However, this is only one possible design for our app – maybe you prefer the old design over this one, or maybe you want to try something else. The point is, you’ve seen how even with the handful of SwiftUI skills you already have it’s possible to build very different designs, and if you have the time I would encourage you to have a play around and see where you end up!

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until July 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.7/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.