---
title: 'SwiftUI Prototype Tutorial 4 of 5: Dynamic Categories & Navigation'
teaser: 'In this installment of our SwiftUI tutorial series, we''ll make our Categories
  list dynamic and add some navigation to it.

  '
tags: swiftui,swift,ios,design
author: Devin Jameson
published_on: 2020-07-09
---

In this installment of our SwiftUI tutorial series, we'll make our
categories list dynamic by displaying a unique image and title on
each card. Then we'll add navigation to our Categories list.

**Posts in this series:**

- [Part 1: Project Setup](https://thoughtbot.com/blog/swiftui-prototype-tutorial-1-of-5-project-setup)
- [Part 2: Category Card View](https://thoughtbot.com/blog/swiftui-prototype-tutorial-2-of-5-category-card-view)
- [Part 3: Categories List](https://thoughtbot.com/blog/swiftui-prototype-tutorial-3-of-5-categories-list)
- [Part 4: Dynamic Categories & Navigation](https://thoughtbot.com/blog/swiftui-prototype-tutorial-4-of-5-dynamic-categories-and-navigation)
- [Part 5: Profile View](https://thoughtbot.com/blog/swiftui-prototype-tutorial-5-of-5-profile-view)

Here's a look at what we'll create in this section.

![The prototype we'll be creating, previewed on an iPhone 11 Pro Max. A cursor is clicking on the screen to navigate to different views. The prototype has two tabs shown at the bottom of the screen: Categories and Profile. The categories tab is blue and the profile tab is black. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The Categories view shows eight clickable categories, each of which is represented by an image associated with that category and an overlayed label with the category name in white text. When the cursor clicks on the Business category, we see a new screen. On the top left of this screen are the headline "Business" and, above that, a blue back button with the text "Categories". The bottom tab bar is still on this screen.](https://images.thoughtbot.com/blog-vellum-image-uploads/l0XxVRBwTrNBqU9KhQEg_1.gif)

Since we want each CategoryCard to receive a different name, we'll start by
creating a category name parameter within our CategoryCard view.

```swift
struct CategoryCard: View {
    let geometry: GeometryProxy
    let categoryName: String
```

Now we can use our categoryName in place of the string "Business" within our
CategoryCard.

```swift
Text(categoryName) // Use categoryName in place of our static string
    .font(.headline)
    .foregroundColor(Color.white)
    .padding(12)
```

We'll see an error in our CategoryRow view because we haven't supplied an
argument to the `categoryName` parameter. Let's fix that. For the first
CategoryCard, we'll set `categoryName` equal to `categoryNameLeft`. For the
second, we'll set `categoryName` equal to `categoryNameRight`. Later, we'll pass
in values for `categoryCardLeft` and `categoryCardRight` from our Categories
view.

```swift
HStack {
    CategoryCard(geometry: geometry, categoryName: categoryNameLeft)
    CategoryCard(geometry: geometry, categoryName: categoryNameRight)
}
```

Now we'll get the error: `Use of unresolved identifier` on both
CategoryCards. To fix this, we'll add parameters for `categoryCardLeft` and
`categoryCardRight` to CategoryRow.

```swift
struct CategoryRow: View {
    let geometry: GeometryProxy
    let categoryNameLeft: String // Add parameter for categoryNameLeft
    let categoryNameRight: String // Add parameter for categoryNameRight

    var body: some View {
```

We have one more error to deal with because we haven't provided arguments for
`categoryNameLeft` and `categoryNameRight` when we use `CategoryRow`. Let's fix
that, too.

```swift
ScrollView {
    VStack {
        CategoryRow(geometry: geometry, categoryNameLeft: "Business", categoryNameRight: "Science")
        CategoryRow(geometry: geometry, categoryNameLeft: "Sports", categoryNameRight: "Opinion")
        CategoryRow(geometry: geometry, categoryNameLeft: "Finance", categoryNameRight: "Politics")
        CategoryRow(geometry: geometry, categoryNameLeft: "Health", categoryNameRight: "Arts")
    }
```

![An iPhone 11 Pro max running our prototype. On the prototype is a bottom tab bar with two options: Categories and Profile. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The categories tab is blue and the profile tab is black. Above the bottom tab bar, there are eight clickable categories, each of which is represented by an image of a foggy New York City taken from above a road, looking down it. There are deep green trees on either side of the road. Overlayed on each image is a label with the category name in white text. The images are rectangular, with more height than width, and they are arranged in two columns and three rows. The images have rounded corners.](https://images.thoughtbot.com/blog-vellum-image-uploads/w3mC8ABmRjBUQyUTvn2p_2.jpg)

Looking good! Now we want to show a different image for each category. Download
the rest of the images
[here](https://images.thoughtbot.com/blog-vellum-image-uploads/KbIAo8IRuSjg3mrSvTqQ_Images.zip)
and add them to your Assets using the process from [Part Two](https://thoughtbot.com/blog/swiftui-prototype-tutorial-2-of-5-category-card-view).

Now that we have access to our images, all we have to do is lowercase our
`categoryName` and use that value, instead of the string "business", to
reference our category image.

```swift
var body: some View {
    ZStack(alignment: .bottomTrailing) {
        Image(categoryName.lowercased()) // Replace our static image
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(width: geometry.size.width * 0.45, height: geometry.size.width * 0.55)
        Text(categoryName)
```

![An iPhone 11 Pro max running our prototype. On the prototype is a bottom tab bar with two options: Categories and Profile. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The categories tab is blue and the profile tab is black. Above the bottom tab bar, there are eight clickable categories, each of which is represented by an image associated with that category and an overlayed label with the category name in white text. The images are rectangular, with more height than width, and they are arranged in two columns and three rows. The images have rounded corners.](https://images.thoughtbot.com/blog-vellum-image-uploads/akQfl5Txmq9wSpe9R27A_3.jpg)

Our Categories view is almost complete! All that's left is to add some
navigation.

In our Categories view, outside GeometryReader, we'll add a `NavigationView`.

```swift
struct Categories: View {
    var body: some View {
        NavigationView { // Add NavigationView
            GeometryReader { geometry in
                ScrollView {
```

![An iPhone 11 Pro max running our prototype. On the prototype is a bottom tab bar with two options: Categories and Profile. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The categories tab is blue and the profile tab is black. Above the bottom tab bar, there are eight clickable categories, each of which is represented by an image associated with that category and an overlayed label with the category name in white text. The images are rectangular, with more height than width, and they are arranged in two columns and three rows. The images have rounded corners. Compared to the previous image, there is more white space above the list of categories.](https://images.thoughtbot.com/blog-vellum-image-uploads/lr3zqu4kRNG13eSSQQjH_4.jpg)

You'll notice some white space appear at the top of our Categories view. This
is where our navigation bar title *would* go. However, we don't want a title
on this screen given that the title is in our TabView. We can hide this space
by using the `.navigationBarTitle()` and `.navigationBarHidden()` methods.

```swift
NavigationView {
    GeometryReader { geometry in
        ScrollView {
            VStack {
                CategoryRow(geometry: geometry, categoryCardLeft: "Business", categoryCardRight: "Science")
                CategoryRow(geometry: geometry, categoryCardLeft: "Sports", categoryCardRight: "Opinion")
                CategoryRow(geometry: geometry, categoryCardLeft: "Finance", categoryCardRight: "Politics")
                CategoryRow(geometry: geometry, categoryCardLeft: "Health", categoryCardRight: "Arts")
            }
            .padding()
        }
        .navigationBarTitle("Categories") // Set the navigation bar title
        .navigationBarHidden(true) // Hide the navigation bar title
    }
}
```

We still have to provide a navigation bar title, but now it will be hidden on
this screen. Note that we must use the `.navigationBarTitle()` and
`.navigationBarHidden()` methods *within* `NavigationView`.

Next, we'll wrap each of our Category Cards in a `NavigationLink`. We have to
provide a `destination` for each NavigationLink, so for now we'll just use a
Text view with an empty string.

```swift
var body: some View {
        HStack {
            NavigationLink(destination: Text("")) {
                CategoryCard(geometry: geometry, categoryName: categoryNameLeft)
            }
            NavigationLink(destination: Text("")) {
                CategoryCard(geometry: geometry, categoryName: categoryNameRight)
            }
        }
    }
```

![An iPhone 11 Pro max running our prototype. On the prototype is a bottom tab bar with two options: Categories and Profile. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The categories tab is blue and the profile tab is black. Above the bottom tab bar, there are eight clickable categories, each of which is represented by a blue rounded rectangle and an overlayed label with the category name in white text. The rectangles have more height than width, and they are arranged in two columns and three rows.](https://images.thoughtbot.com/blog-vellum-image-uploads/jDV6LAsaQw6lpO5dLc2b_5.jpg)

It appears the Blue Man Group has invaded our device. To fix our category card
styling, we can change the `buttonStyle` of our navigation links.

```swift
HStack {
    NavigationLink(destination: Text("")) {
        CategoryCard(geometry: geometry, categoryName: categoryCardLeft)
    }
    NavigationLink(destination: Text("")) {
        CategoryCard(geometry: geometry, categoryName: categoryCardRigh
    }
}
.buttonStyle(PlainButtonStyle()) // Change button style of navigation links
```

We can now click on each Category and be brought to a blank screen. As a final
step, we'll swap out this blank screen and instead show the name of the clicked
category.

![Our prototype running on an iPhone 11 Pro Max. A cursor is clicking on the screen to navigate to different views. The prototype has two tabs shown at the bottom of the screen: Categories and Profile. The categories tab is blue and the profile tab is black. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The Categories view shows eight clickable categories, each of which is represented by an image associated with that category and an overlayed label with the category name in white text. When the cursor clicks on a category, we see a new screen. On the top left of this screen is a blue back button with the text "Categories". The bottom tab bar is still on this screen. The cursor then clicks on the back button to navigate to the prior screen.](https://images.thoughtbot.com/blog-vellum-image-uploads/TEz3rWhhQAK3QJcxfnHv_6.gif)

First, we'll create a view for our Category screen. We don't want any content on
this page besides the navigation bar title, so we'll just add a `VStack` with an
empty Text view.

```swift
struct Category: View {
    var body: some View {
        VStack {
            Text("")
        }
    }
}
```

Now let's supply our new Category view to our NavigationLink destination
parameters.

```swift
HStack {
    NavigationLink(destination: Category()) { // Change the destination to Category()
        CategoryCard(geometry: geometry, categoryName: categoryNameLeft)
    }
    NavigationLink(destination: Category()) { // Change the destination to Category()
        CategoryCard(geometry: geometry, categoryName: categoryNameRight)
    }
}
```

To display the category name in the Category view, we'll just pass the
`categoryName` through to the Category View and supply it to the
`navigationBarTitle` method.

```swift
HStack {
    NavigationLink(destination: Category(categoryName: categoryNameLeft)) { // Pass the categoryName to Category()
        CategoryCard(geometry: geometry, categoryName: categoryNameLeft)
    }
    NavigationLink(destination: Category(categoryName: categoryNameRight)) { // Pass the categoryName to Category()
        CategoryCard(geometry: geometry, categoryName: categoryNameRight)
    }
}
```

```swift
struct Category: View {
    let categoryName: String // Add a parameter for the category name
    
    var body: some View {
        VStack {
            Text("")
        }
        .navigationBarTitle(categoryName) // Use the category name
    }
}
```

![The prototype we'll be creating, previewed on an iPhone 11 Pro Max. A cursor is clicking on the screen to navigate to different views. The prototype has two tabs shown at the bottom of the screen: Categories and Profile. The categories tab is blue and the profile tab is black. Above the categories tab label is a globe icon, and above the profile tab label is a person icon. The Categories view shows eight clickable categories, each of which is represented by an image associated with that category and an overlayed label with the category name in white text. When the cursor clicks on the Business category, we see a new screen. On the top left of this screen are the headline "Business" and, above that, a blue back button with the text "Categories". The bottom tab bar is still on this screen.](https://images.thoughtbot.com/blog-vellum-image-uploads/l0XxVRBwTrNBqU9KhQEg_1.gif)

That's it for this section! See you for
[Part Five](https://thoughtbot.com/blog/swiftui-prototype-tutorial-5-of-5-profile-view),
in which we'll create our Profile view.
