Search code examples
iosswiftxcodeswiftuinavigationview

How to properly include "Add Item" button in a NavigationView using SwiftUI?


I need a "Plus" ⊕ Button in my NavigationView's List .navigationBarItems (right on the navigation bar), which I'd like to add a row to the list, using a subsequent view in the navigation hierarchy to enter its name etc.

enter image description here

But first, I can't even get the button to navigate correctly! When I tap it in the Preview Canvas, the button's operation seems to work. Yet in an actual app, while it does navigate to my destination View, when I tap that View's "< Back" button, the app crashes with:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Tried to pop to a view controller that doesn't exist.'

Any suggestions how I may fix the following code, please? This task is so common, surely I'm missing something and doing it wrong.

import SwiftUI

struct ListItem: Identifiable {
    var id = UUID()
    var name: String
}

struct ContentView: View {
    var listItems = [
        ListItem(name: "List Item One"),
        ListItem(name: "List Item Two")
    ]

    var body: some View {
        NavigationView {
            List(listItems) { listItem in
                NavigationLink(destination: DetailView(existingItem: listItem)) {
                    Text(listItem.name)
                }
            }
            .navigationBarTitle(Text("Configure List Items"), displayMode: .inline)
            .navigationBarItems(trailing:
                    NavigationLink(destination: DetailView(existingItem: nil)) {
                                        Image(systemName: "plus")
                                            .resizable()
                                            .padding(6)
                                            .frame(width: 24, height: 24)
                                            .background(Color.blue)
                                            .clipShape(Circle())
                                            .foregroundColor(.white)
                                    } )
        }
    }
}

struct DetailView: View {
    var existingItem: ListItem?

    var body: some View {
        Text((existingItem != nil) ? "Edit existing: \(existingItem!.name)" : "Enter NEW List Item")
    }
}

Thank you! By the way, I'm on macOS Catalina 10.15.2 using: Xcode 11.3.1


Solution

  • NavigationLink should be inside NavigationView, not in navigation bar, so the following approach works...

    @State private var addMode = false
    var body: some View {
        NavigationView {
            VStack {
                List(listItems) { listItem in
                    NavigationLink(destination: AddDetailView(existingItem: listItem)) {
                        Text(listItem.name)
                    }
                }
                .navigationBarTitle(Text("Configure List Items"), displayMode: .inline)
                .navigationBarItems(trailing: Button(action: { 
                     // button activates link
                      self.addMode = true 
                    } ) {
                    Image(systemName: "plus")
                        .resizable()
                        .padding(6)
                        .frame(width: 24, height: 24)
                        .background(Color.blue)
                        .clipShape(Circle())
                        .foregroundColor(.white)
                } )
    
                // invisible link inside NavigationView for add mode
                NavigationLink(destination: AddDetailView(existingItem: nil), 
                    isActive: $addMode) { EmptyView() }
            }
        }
    }