1
votes

I'm attempting to create a master/detail view on macOS with SwiftUI. When the master/detail view first renders, I'd like it to immediately "highlight" / "navigate to" its first entry.

In other words, I'd like to immediately render the following: master/detail first row highlighted

I'm using NavigationView and NavigationLink on macOS to render the master/detail view:

struct ContentView: View {

  var body: some View {
    NavigationView {
      List {
        NavigationLink(destination: Text("detail-1").frame(maxWidth: .infinity, maxHeight: .infinity)) {
          Text("link-1")
        }
        NavigationLink(destination: Text("detail-2").frame(maxWidth: .infinity, maxHeight: .infinity)) {
          Text("link-2")
        }
      }
    }
  }
}

I've tried using both the isActive and the tag / selection options provided by NavigationLink with no luck. What might I be missing here? Is there a way to force focus on the first master/detail element using SwiftUI?

1
Known issue. You can submit feedback to Apple to make it fixed faster. - Asperi
Thanks @Asperi - is there some publicly available link that describes this as being a known bug? I'll submit through feedback assistant in the meantime - David Muller
I'm seeing the same issue. Let me know if you have the link to the filed bug. - Kon

1 Answers

0
votes

I came across this problem recently and after being stuck at the same point I found Apple's tutorial which shows that you don't use NavigationLink on macOS.

Instead you just create a NavigationView with a List and a DetailView. Then you can bind the List's selection and it works properly.

There still seems to be a bug with the highlight. The workaround is setting the selection in the next run loop after the NavigationView has appeared. :/

Here's a complete example:

enum DetailContent: Int, CaseIterable, Hashable {
    case first, second, third
}
extension DetailContent: Identifiable {
    var id: Int { rawValue }
}

struct DetailView: View {
    @Binding var content: DetailContent?

    var body: some View {
        VStack {
            Text("\(content?.rawValue ?? -1)")
        }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

struct MainView: View {
    @State var detailContent: DetailContent?

    var body: some View {
        NavigationView {
            List(selection: $detailContent) {
                Section(header: Text("Section")) {
                    ForEach(DetailContent.allCases) { item in
                        Text("\(item.rawValue)")
                            .tag(item)
                    }
                }
            }
                .frame(minWidth: 250, maxWidth: 350)
                .listStyle(SidebarListStyle())
            if detailContent != nil {
                DetailView(content: $detailContent)
            }
        }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .onAppear {
                DispatchQueue.main.async {
                    self.detailContent = DetailContent.allCases.randomElement()
                }
            }
    }
}