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

Showing mission details with ScrollView and GeometryReader

Paul Hudson    @twostraws   

When the user selects one of the Apollo missions from our main list, we want to show information about the mission: its image, its mission badge, and all the astronauts that were on the crew along with their roles. The first two of those aren’t too hard, but the second requires more work because we need to match up crew IDs with crew details across our two JSON files.

Let’s start simple and work our way up: make a new SwiftUI view called MissionView.swift. Initially this will just have a mission property so that we can show the mission badge and description, but shortly we’ll add more to it.

In terms of layout, this thing needs to have a scrolling VStack with a resizable image for the mission badge, then a text view, then a spacer so that everything gets pushed to the top of the screen. We’ll use GeometryReader to set the maximum width of the mission image, although through some trial and error I found that the mission badge worked best when it wasn’t full width – somewhere between 50% and 75% width looked better, to avoid it becoming weirdly big on the screen.

Put this code into MissionView.swift now:

struct MissionView: View {
    let mission: Mission

    var body: some View {
        GeometryReader { geometry in
            ScrollView(.vertical) {
                VStack {
                        .frame(maxWidth: geometry.size.width * 0.7)


                    Spacer(minLength: 25)
        .navigationBarTitle(Text(mission.displayName), displayMode: .inline)

Did you notice that the spacer was created with minLength: 25? This isn’t something we’ve used before, but it ensures the spacer has a minimum height of at least 25 points. This is helpful inside scroll views because the total available height is flexible: a spacer would normally take up all available remaining space, but that has no meaning inside a scroll view.

We could have accomplished the same result using Spacer().frame(minHeight: 25), but using Spacer(minLength: 25) has the advantage that if you ever change your stack orientation – if you go from a VStack to a HStack, for example – then it effectively becomes Spacer().frame(minWidth: 25).

Anyway, with our new view in place the code will no longer build, all because of the previews struct below it – that thing needs a Mission object passed in so it has something to render. Fortunately, our Bundle extension is available here as well:

struct MissionView_Previews: PreviewProvider {
    static let missions: [Mission] = Bundle.main.decode("missions.json")

    static var previews: some View {
        MissionView(mission: missions[0])

If you look in the preview you’ll see that’s a good start, but the next part is trickier: we want to show the list of astronauts who took part in the mission below the description. Let’s tackle that next…

SAVE 20% ON iOS CONF SG The largest iOS conference in Southeast Asia is back in Singapore for the 5th time in January 2020, now with two days of workshops plus two days of talks on SwiftUI, Combine, GraphQL, and more! Save a massive 20% on your tickets by clicking on this link.

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: 5.0/5