Как правильно обрабатывать выбор в NavigationView на macOS? - PullRequest
1 голос
/ 30 апреля 2020

Я хочу создать простое приложение MacOS с боковой панелью и некоторым содержимым в соответствии с выбором на боковой панели.

У меня есть MainView, который содержит NavigationView с SidebarListStyle. Он содержит List с некоторыми NavigationLink с. У них есть привязка для выбора.

Я ожидаю, что сработают следующие вещи:

  1. Когда я запускаю свое приложение, значение выбора игнорируется. Нет ни подсветки для элемента на боковой панели, ни содержимого на панели сведений.

  2. Когда я вручную выбираю элемент на боковой панели, должна быть возможность навигации с помощью кнопок вверх / вниз клавиши со стрелками между элементами. Это не работает, поскольку выделение / выделение исчезают.

  3. Когда я обновляю значение привязки выделения, оно должно выделять элемент в списке, чего не происходит.

Вот мой пример реализации:

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

class NavigationRouter: ObservableObject {
    @Published var selection: DetailContent?
}

struct DetailView: View {
    @State var content: DetailContent
    @EnvironmentObject var navigationRouter: NavigationRouter

    var body: some View {
        VStack {
            Text("\(content.rawValue)")
            Button(action: { self.navigationRouter.selection = DetailContent.allCases.randomElement()!}) {
                Text("Take me anywhere")
            }
        }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

struct MainView: View {
    @ObservedObject var navigationRouter = NavigationRouter()
    @State var detailContent: DetailContent? = .first

    var body: some View {
        NavigationView {
            List {
                Section(header: Text("Section")) {
                    ForEach(DetailContent.allCases) { item in
                        NavigationLink(
                            destination: DetailView(content: item),
                            tag: item,
                            selection: self.$detailContent,
                            label: { Text("\(item.rawValue)") }
                        )
                    }
                }
            }
            .frame(minWidth: 250, maxWidth: 350)
        }
            .environmentObject(navigationRouter)
            .listStyle(SidebarListStyle())
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .onReceive(navigationRouter.$selection) { output in
                self.detailContent = output
            }
    }
}

EnvironmentObject используется для распространения изменения внутри DetailView. Если есть лучшее решение, я очень рад услышать об этом.

Таким образом, остается вопрос: что я делаю неправильно, если это происходит? У меня была некоторая надежда, что с Xcode 11.5 Beta 1 это будет go далеко, но это не так.

1 Ответ

1 голос
/ 10 мая 2020

После нахождения учебника от Apple выяснилось, что вы не используете NavigiationLink в macOS. Вместо этого вы связываете список и добавляете два представления к NavigationView.

С этими обновлениями к MainView и DetailView мой пример работает отлично:

struct DetailView: View {
    @Binding var content: DetailContent?
    @EnvironmentObject var navigationRouter: NavigationRouter

    var body: some View {
        VStack {
            Text("\(content?.rawValue ?? -1)")
            Button(action: { self.navigationRouter.selection = DetailContent.allCases.randomElement()!}) {
                Text("Take me anywhere")
            }
        }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

struct MainView: View {
    @ObservedObject var navigationRouter = NavigationRouter()
    @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())
            DetailView(content: $detailContent)
        }
            .environmentObject(navigationRouter)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .onReceive(navigationRouter.$selection) { output in
                DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) {
                    self.detailContent = output
                }
            }
    }
}
...