Skip to Content
Course content

216: Building a Split-View Master-Detail App for iPad

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a PR for a colleague who had just finished a polished note-taking app. It worked beautifully on the iPhone, but when he ran it on an iPad Pro, it looked... well, lonely. He had just let the views scale up, leaving massive gutters of white space and a single column of text that stretched across the entire 12.9-inch screen. It felt like looking at a giant postcard. He asked me, "Why does it feel so empty?" The answer was simple: he wasn't utilizing the screen real estate. On a tablet, users expect a "Master-Detail" flow—the ability to see their list and their content side-by-side without constant back-and-forth navigation.

Designing the Three-Column Architecture

In modern SwiftUI, the NavigationSplitView is your primary tool for solving this "empty space" problem. It allows you to define a hierarchy—usually a sidebar, a content list, and a detail view—that the system automatically manages based on the device's screen size. For our example, let's imagine we're building a Recipe Book. On an iPad, you want your categories (Breakfast, Dinner, Dessert) in the sidebar, the list of recipes for that category in the middle, and the actual cooking instructions in the detail pane.

struct RecipeAppView: View {
    @State private var selectedCategory: RecipeCategory?
    @State private var selectedRecipe: Recipe?

    var body: some View {
        NavigationSplitView {
            List(categories, selection: $selectedCategory) { category in
                Text(category.name).tag(category)
            }
            .navigationTitle("Categories")
        } content: {
            if let category = selectedCategory {
                List(category.recipes, selection: $selectedRecipe) { recipe in
                    Text(recipe.title).tag(recipe)
                }
                .navigationTitle(category.name)
            } else {
                Text("Select a category")
            }
        } detail: {
            if let recipe = selectedRecipe {
                RecipeDetailView(recipe: recipe)
            } else {
                Text("Select a recipe to get started")
            }
        }
    }
}

Binding Selection for Seamless Transitions

The magic here lies in the selection bindings. Notice how I used $selectedCategory and $selectedRecipe. By tagging each item in the list with the object itself, SwiftUI knows exactly which item is active. When the user taps a category in the sidebar, the content column updates instantly. When they tap a recipe, the detail view swaps in.

I've found that the biggest mistake developers make is forgetting to handle the "empty state." If you don't provide a fallback view in the detail closure (like the "Select a recipe" text above), the user is greeted with a blank screen upon launch. It feels broken, even if it's technically working. Always give your user a hint about what they're supposed to do next.

Adapting to the Compact Column Style

One of the best things about NavigationSplitView is that you don't have to write separate code for iPhone and iPad. On an iPhone, SwiftUI automatically collapses this three-column layout into a single-column stack. The sidebar becomes the first screen, the content list becomes the second, and the detail view the third.

However, you can control this behavior using NavigationSplitViewVisibility. If you want the sidebar to be hidden by default on iPad to give more room to the content, you can pass a visibility state to the initializer. I usually recommend leaving it to the system default unless your app is content-heavy (like a photo editor), where the sidebar is secondary to the main workspace.




📋 Practical Task

The Gourmet Recipe Browser Layout

Your task is to build a functional three-column split view for a gourmet recipe application. You will need to implement the following:

  • Data Model: Create a RecipeCategory struct (with a name and an array of recipes) and a Recipe struct (with a title and a description). Both must conform to Hashable and Identifiable.
  • Sidebar: A list of at least three categories.
  • Content Column: A list that displays the recipes belonging to the currently selected category.
  • Detail Column: A view that displays the full description of the selected recipe.
  • Empty States: Ensure that both the content and detail columns show a descriptive Text view (e.g., "Please select a category") when no item is selected.

Test your implementation by running it in the iPad Pro simulator to verify the three-column layout, and then in the iPhone 15 simulator to ensure it collapses into a standard navigation stack.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.