NEW: Nominations are now open for the 2019 Swift Community Awards! >>

Stacking up buttons

Paul Hudson    @twostraws   

We’re going to start our app by building the basic UI structure, which will be two labels telling the user what to do then three image buttons showing three world flags.

First, find the assets for this project and drag them into your asset catalog. That means opening Assets.xcassets in Xcode, then dragging in the flag images from the project2-files folder. You’ll notice that the images are named after their country, along with either @2x or @3x – these are images at double resolution and triple resolution to handle different types of iPhone screen.

Next, we need two properties to store our game data: an array of all the country images we want to show in the game, plus an integer storing which country image is correct.

var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Russia", "Spain", "UK", "US"]
var correctAnswer = Int.random(in: 0...2)

The Int.random(in:) method automatically picks a random number, which is perfect here – we’ll be using that to decide which country flag should be tapped.

Inside our body, we need to lay out our game prompt in a vertical stack, so let’s start with that:

var body: some View {
    VStack {
        Text("Tap the flag of")
        Text(countries[correctAnswer])
    }
}

Below there we want to have our tappable flag buttons, and while we could just add them to the same VStack we can actually create a second VStack so that we have more control over the spacing.

The VStack we just created above holds two text views and has no spacing, but the flags are going to have 30 points of spacing between them so it looks better.

So, start by adding this ForEach loop directly below the end of the VStack we just created:

ForEach(0 ..< 3) { number in
    Button(action: {
       // flag was tapped
    }) {
        Image(self.countries[number])
            .renderingMode(.original)
    }
}

The renderingMode(.original) modifier tells SwiftUI to render the original image pixels rather than trying to recolor them as a button.

And now we have a problem: our body property is trying to send back two views, a VStack and a ForEach, but that isn’t allowed. This is where our second VStack will come in: I’d like you to wrap the original VStack and the ForEach below in a new VStack, this time with a spacing of 30 points.

So your code should look like this:

var body: some View {
    VStack(spacing: 30) {
        VStack {
            Text("Tap the flag of")
            // etc
        }

        ForEach(0 ..< 3) { number in
            // etc
        }
    }
}

Having two vertical stacks like this allows us to position things more precisely: the outer stack will space its views out by 30 points each, whereas the inner stack has no spacing.

That’s enough to give you a basic idea of our user interface, and already you’ll see it doesn’t look great – some flags have white in them, which blends into the background, and all the flags are centered vertically on the screen.

We’ll come back to polish the UI later, but for now let’s put in a blue background color to make the flags easier to see. Because this means putting something behind our outer VStack, we need to use a ZStack as well. Yes, we’ll have a VStack inside another VStack inside a ZStack, and that is perfectly normal.

Start by putting a ZStack around your outer VStack, like this:

var body: some View {
    ZStack {
        // previous VStack code
    }
}

Now put this just inside the ZStack, so it goes behind the outer VStack:

Color.blue.edgesIgnoringSafeArea(.all)

That edgesIgnoringSafeArea() modifier ensures the color goes right to the edge of the screen.

Now that we have a darker background color, we should give the text something brighter so that it stands out better:

Text("Tap the flag of")
    .foregroundColor(.white)

Text(countries[correctAnswer])
    .foregroundColor(.white)

The last change we’ll make, for now at least, is to push upwards all the things in our outer VStack, so the UI sits next to the top of the screen. This is as simple as adding a spacer view directly after the end of the ForEach:

Spacer()

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift 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