Cameron
Cameron

Reputation: 712

NavigationLink on macOS not opening in the same View

I'm currently building a macOS App with SwiftUI (no Catalyst) that is supposed to have a Sidebar and a single View to the right of it.

NavigationView {
    List {
        ...
    }
    .listStyle(SidebarListStyle())

    HomeView()
}

My Home View has a NavigationLink inside of it, pointing to another DetailView.

struct HomeView: View {
    var body: some View {
        NavigationLink("DetailView", destination: Text("This is the DV"))
    }
}

This NavigationLink always appears as disabled and only works when I add another Column to the NavigationView. I don't want another Column, but rather the NavigationLink replacing the HomeView with the DetailView.

Is there any way to achieve this?

Upvotes: 5

Views: 1947

Answers (2)

Cameron
Cameron

Reputation: 712

So apparently SwiftUI 2 does not support pushing views on to the Navigation Stack on macOS yet. However, I found this package that helps to resolve the issue:

github.com/lbrndnr/StackNavigationView

Upvotes: 2

JuJoDi
JuJoDi

Reputation: 14955

NavigationLinks always open a detail view from the current "column" of the navigation view, so if your navigation link is rendered as a child view of HomeView then it will try to open as a detail view of HomeView (the missing third column). If you want a view to replace the HomeView the intended way then the user should select something from the List. If you're trying to programmatically select a NavigationLink that already exists in your list then you should do so by changing state within the HomeView of a variable that is bound to the selected item of the list or the selection of the NavigationLink within the list. Here's a complete working example that does both within the ContentView.swift of a new MacOS SwiftUI Xcode project

struct HomeView: View {
    @Binding var selectedItem: String?
    
    var body: some View {
        Button("Hello") {
            selectedItem = "Thing2"
        }
    }
}

struct ContentView: View {
    @State private var selectedItem: String?
    private let items = ["Thing1", "Thing2"]
    
    var body: some View {
        NavigationView {
            List(selection: $selectedItem) {
                ForEach(items, id: \.self) {item in
                    NavigationLink(
                        destination: Text(item),
                        tag: item,
                        selection: $selectedItem
                    ) {
                        Text(item)
                    }
                }
            }
            HomeView(selectedItem: $selectedItem)
        }
    }
}

Here is the application displaying the HomeView

HomeView

And here is the application displaying the selected thing after pressing the button

Selected Thing

If you don't want to show anything selected in the list after navigating the view, but still want to use a navigation link to maintain the integrity of the navigation within the NavigationView then you could technically put a hidden navigation link in the list

NavigationView {
            List(selection: $selectedItem) {
                ForEach(items, id: \.self) {item in
                    NavigationLink(
                        destination: Text(item),
                        tag: item,
                        selection: $selectedItem
                    ) {
                        Text(item)
                    }
                }
                NavigationLink(
                    destination: Text("Sneaky"),
                    tag: "Sneaky",
                    selection: $selectedItem
                ) {
                    Text("")
                }.hidden()
            }
            HomeView(selectedItem: $selectedItem)
        }

In that case make sure your button also selects the sneaky destination link

Button("Hello") {
  selectedItem = "Sneaky"
}

Upvotes: 0

Related Questions