swiftswiftuisidebar

How to customize a NavigationSplitView with a Title in SwiftUI


I am trying to create a customizable NavigationSplitView with a title and a fully customizable sidebar in SwiftUI, similar to the one shown in this sidebar example: enter image description here

Problem

I am struggling to reproduce the sidebar as shown in the reference image. Instead, my implementation only looks like this: enter image description here

Goal

I want to create a sidebar that allows for full customization, including the use of ScrollView instead of List, and with a navigation title.

Expected Result

A fully customizable sidebar with a title that matches the reference image.

Actual Result

The sidebar is not fully customizable and does not resemble the reference image.

What I've Tried

Using ScrollView in the sidebar instead of List. Trying different approaches with List.

Minimal code to reproduce:

import SwiftUI
import SwiftData

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .background(.white)
        }
    }
}

//
//  ContentView.swift
//  DeployApp
//
//  Created by Lucas Sesti on 20/07/24.
//

import SwiftUI
import SwiftData

struct ContentView: View {    
    var body: some View {
        NavigationSplitView {
            List {
                Text("Item 1")
                Text("Item 2")
            }
            .navigationTitle("Caixa de Entrada")
        } detail: {
            Text("Detail view")
        }
        .navigationTitle("Titulo do detail view")
    }
}




Solution

  • The first screenshot looks like it's running on "Mac (Designed for iPad)", but it seems you are running your app on native macOS, which doesn't natively support this.

    You should add "Mac (Designed for iPad)" destination in the project settings, and select the "My Mac (Designed for iPad)" run destination.

    enter image description here

    After that, the title "Caixa de Entrada" would show up on the sidebar view. For the detail view's title, you should move .navigationTitle("Titulo do detail view") inside the detail: { ... } closure. It should not be modifying the NavigationSplitView.


    Here I tried to recreate the whole sidebar in the first screenshot, with "add" buttons that adds UUIDs to the list.

    struct ContentView: View {
        @State var listItems: [UUID] = []
        @State var selected: UUID?
        
        var body: some View {
            NavigationSplitView {
                Group {
                    if listItems.isEmpty {
                        ContentUnavailableView {
                            Label("Seus Pensamentos Não Estruturados", systemImage: "tray.fill")
                        } description: {
                            Text("Registre tarefas e pensamentos conforme eles surgem. Mova-as para sua linha do tempo quando você estiver pronto para agendar.")
                        } actions: {
                            // I adjusted the color here with saturation and brightness
                            // so that this looks nice with the default blue accent color
                            // In your screenshot it seems like they changed their AccentColor
                            // asset in their asset catalog to a less saturated blue
                            Button {
                                listItems.append(UUID())
                            } label: {
                                Label("Nova Tarefa da Caixa de Entrada", systemImage: "plus.circle.fill")
                                    .padding()
                                    .foregroundStyle(Color.accentColor)
                                    .brightness(0.3)
                            }
                            .background(
                                Color.accentColor.opacity(0.5),
                                in: RoundedRectangle(cornerRadius: 10)
                            )
                            .saturation(0.5)
                        }
                    } else {
                        List(selection: $selected) {
                            ForEach(listItems, id: \.self) {
                                Text("\($0)")
                            }
                        }
                    }
                }
                .navigationTitle("Caixa de Entrada")
                .toolbar {
                    ToolbarItem(placement: .primaryAction) {
                        Button {
                            listItems.append(UUID())
                        } label: {
                            Image(systemName: "plus.circle.fill")
                        }
                    }
                }
            } detail: {
                Group {
                    if let selected {
                        Text("Details for \(selected)...")
                    } else {
                        ContentUnavailableView {
                            Text("Please select an item")
                        }
                    }
                }
                .navigationTitle("Titulo do detail view")
            }
        }
    }
    

    Output:

    enter image description here