1

I'm building an iOS app with SwiftUI and am encountering unexpected behaviour when navigating from a List view to a detail view.

I have three views: ViewA, ViewB, and ViewC. ViewA navigates to ViewB, which is a list of Item instances. Tapping on an Item in ViewB should navigate to ViewC, presenting the details of the selected Item.

The navigation to ViewC is misbehaving. When I tap an Item in ViewB, ViewC is shown briefly and then the view immediately navigates back to ViewB. The navigation stack has ViewC before ViewB. Stack: [ViewA, ViewC, ViewB]

The minimal reproducible example:

struct Item: Identifiable, Hashable {
    let id = UUID()
    let title: String
    let description: String
}

struct ViewA: View {
    var body: some View {
        NavigationStack {
            NavigationLink("Go to ViewB") {
                ViewB()
            }
        }
    }
}

struct ViewB: View {
    let items: [Item] = [
        Item(title: "Item 1", description: "This is item 1"),
        Item(title: "Item 2", description: "This is item 2")
    ]
    
    var body: some View {
        List(items, id: \.id) { item in
            NavigationLink(value: item) {
                Text(item.title)
            }
        }
        .navigationDestination(for: Item.self) { item in
            ViewC(item: item)
        }
    }
}

struct ViewC: View {
    let item: Item

    var body: some View {
        Text(item.description)
    }
}

I expect to navigate directly to ViewC when an Item is selected in ViewB and the back button should take me back to ViewB. The stack should be [ViewA (root), ViewB, ViewC].

What caused the misbehaviour and how can I resolve this?

Ci Leong
  • 92
  • 11

1 Answers1

1

You can modify the code of ViewB as follows:

struct ViewB: View {
    let items: [Item] = [
        Item(title: "Item 1", description: "This is item 1"),
        Item(title: "Item 2", description: "This is item 2")
    ]
    
    var body: some View {
        List(items) { item in
            NavigationLink {
                ViewC(item: item)
            } label: {
                Text(item.title)
            }
        }
    }
}
baohoang
  • 616
  • 1
  • 5
  • 13
  • Confirmed that this works! Could you explain why this solves the issue, and if `.navigationDestination` could be used here in any way? – Ci Leong Jun 01 '23 at 06:40
  • 1
    .navigationDestination must be inside a NavigationStack, but we can't add a NavigationStack in ViewB because ViewB is a child view of ViewA, and having double NavigationStacks is incorrect. Therefore, we need to modify the code of ViewB as shown above. – baohoang Jun 01 '23 at 07:50
  • Isn't ViewB inside of the NavigationStack? Must it be exactly one level under the NavigationStack? – Ci Leong Jun 01 '23 at 07:56
  • 1
    Exactly, .navigationDestination must be used within the same view hierarchy of the NavigationStack view. – baohoang Jun 01 '23 at 09:09