Search code examples
swiftswiftui-navigationstack

Returning to 'Home' View with NavigationStack


I'm trying to navigate to my 'home' view when the user clicks a button a few screens into the NavigationStack. My skeleton code is below and I can't figure out why it doesn't return to View1 after clicking the "Go Home" button on View3.

import Foundation
import SwiftUI
import SwiftData

@Model
final class Item {
    var name: String
    
    init(name: String = "") {
        self.name = name
    }
}

struct View1: View{
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    
    @State private var navigationPath: NavigationPath = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        View2(navigationPath: $navigationPath, item: item)
                    } label: {
                        Text(item.name)
                    }
                }
            }
        }
    }
}

struct View2: View{
    @Binding var navigationPath: NavigationPath
    @Bindable var item: Item
    
    @State var isPresentingNewItemView: Bool = false
    
    var body: some View{
        Text(item.name)
            .toolbar {
                Button("Edit", action: editItem)
            }
            .sheet(isPresented: $isPresentingNewItemView) {
                View3(navigationPath: $navigationPath, item: item)
            }
    }
    func editItem() {
        isPresentingNewItemView = true
    }
}

struct View3: View{
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    
    @Binding var navigationPath: NavigationPath
    @Bindable var item: Item
    
    var body: some View{
        Text(item.name)
 
        Button("Go Home"){
            NavigationLink("Home") {
                View1()
            }
            dismiss()
            navigationPath = NavigationPath()
        }
    }
}

#Preview {
    do{
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try! ModelContainer(for: Item.self, configurations: config)
        
        for i in 1..<10 {
            let item = Item(name: "Example Item \(i)")
            container.mainContext.insert(item)
        }
        
        return View1()
            .modelContainer(container)
    } catch {
        fatalError("Failed to create model container.")
    }
}

I'm a newbie when it comes to coding in general so any feedback on your thought process is appreciated.


Solution

  • To make your View3 "Go Home" button work, try this approach using NavigationLink(..., value: ...) and a navigationDestination(...) as shown in the example code.

    struct View1: View {
        @Environment(\.modelContext) private var modelContext
        @Query private var items: [Item]
        
        @State private var navigationPath = NavigationPath()
        
        var body: some View {
            NavigationStack(path: $navigationPath) {
                List {
                    ForEach(items) { item in
                        NavigationLink("\(item.name)", value: item) // <--- here
                    }
                }
                .navigationDestination(for: Item.self) { item in  // <--- here
                    View2(navigationPath: $navigationPath, item: item)
                }
            }
        }
    }
    
     struct View2: View{
         @Binding var navigationPath: NavigationPath
         @Bindable var item: Item
         
         @State var isPresentingNewItemView: Bool = false
         
         var body: some View{
             Text(item.name)
                 .toolbar {
                     Button("Edit", action: editItem)
                 }
                 .sheet(isPresented: $isPresentingNewItemView) {
                     View3(navigationPath: $navigationPath, item: item)
                 }
         }
         func editItem() {
             isPresentingNewItemView = true
         }
     }
    
     struct View3: View{
         @Environment(\.modelContext) private var modelContext
         @Environment(\.dismiss) private var dismiss
         
         @Binding var navigationPath: NavigationPath
         @Bindable var item: Item
         
         var body: some View{
             Text(item.name)
             TextField("", text: $item.name) // <--- for testing
             Button("Go Home"){ // <--- here
                 dismiss()
                 navigationPath = NavigationPath()
             }
         }
     }