Проблемы обновления / передачи EnvironmentObject с помощью NavigationLink

В настоящее время я разрабатываю приложение для watchOS 6 (независимое приложение) с использованием Swift/SwiftUI в XCode 11.5 на macOS Catalina.

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

После завершения процесса настройки пользователь должен нажать кнопку, чтобы вернуться в "главное" приложение (главное представление). Для управления представлениями, которые находятся на одном иерархическом уровне, я планировал использовать EnvironmentObject (насколько я понял, один раз внедренный EnvironmentObject передается вложенным представлениям, а вложенные представления могут использовать EnvironmentObject) в сочетании с "управляющим представлением" который управляет отображением представлений. Поэтому я следовал руководству: https://blckbirds.com/post/how-to-navigate-between-views-in-swiftui-by-using-an-environmentobject/

Это мой код:

ContentView.swift

struct ContentView: View {
    var body: some View {
        ContentViewManager().environmentObject(AppStateControl())        
    }
}

struct ContentViewManager: View {
    @EnvironmentObject var appStateControl: AppStateControl
    
    var body: some View {
        VStack {
            if(appStateControl.callView == "AppConfig") {
                AppConfig()
            }
            if(appStateControl.callView == "AppMain") {
                AppMain()
            }
        }
    }
}

AppStateControl.swift

class AppStateControl: ObservableObject {
    @Published var callView: String = "AppConfig"
}

AppConfig.swift

struct AppConfig: View {
    @EnvironmentObject var appStateControl: AppStateControl
    var body: some View {
        VStack {
            Text("App Config Main")
            NavigationLink(destination: DetailView1().environmentObject(appStateControl)) {
                Text("Show Detail View 1")
            }
        }
    }
}

struct DetailView1: View {
    @EnvironmentObject var appStateControl: AppStateControl
    var body: some View {
        VStack {
            Text("App Config Detail View 1")
            NavigationLink(destination: DetailView2().environmentObject(appStateControl)) {
                Text("Show Detail View 2")
            }
        }
    }
}

struct DetailView2: View {
    @EnvironmentObject var appStateControl: AppStateControl
    var body: some View {
        VStack {
            Text("App Config Detail View 2")
            Button(action: {
                self.appStateControl.callView = "AppMain"
            }) {
             Text("Go to main App")
            }
        }
    }
}

AppMain.swift

struct AppMain: View {
    var body: some View {
        Text("Main App")
    }
}

В предыдущей версии моего кода (без постоянной передачи EnvironmentObject) я получал ошибку времени выполнения ("Поток 1: Неустранимая ошибка: не обнаружен ObservableObject типа AppStateControl. View.environmentObject(_:) для AppStateControl может отсутствовать как предок этого представления. ") вызвано строкой 41 в AppConfig.swift. В Интернете я прочитал, что это, вероятно, ошибка NavigationLink (см.: https://www.hackingwithswift.com/forums/swiftui/environment-object-not-being-inherited-by-child-sometimes-and-app-crashes/269, https://twitter.com/twostraws/status/1146315336578469888). Таким образом, была рекомендована явная передача EnvironmentObject в пункт назначения NavigationLink (реализация выше). К сожалению, это также не работает, и вместо этого щелчок по кнопке "Перейти к основному приложению" в "DetailView2" приводит к представлению "DetailView1" вместо "AppMain".

Есть идеи, как решить эту проблему? Мне кажется, что изменение EnvironmentObject в представлении, вызываемом через ссылку навигации, не обновляет представления (правильно).

Заранее спасибо.

1 ответ

Решение

Одно из решений - создать переменную, определяющую, отображать ли стек навигации.

class AppStateControl: ObservableObject {
    ...
    @Published var isDetailActive = false // <- add this
}

Затем вы можете использовать эту переменную для управления первым NavigationLink установив isActiveпараметр. Также вам нужно добавить.isDetailLink(false) ко всем последующим ссылкам.

Первая ссылка в стеке:

NavigationLink(destination: DetailView1().environmentObject(appStateControl), isActive: self.$appStateControl.isDetailActive) {
    Text("Show Detail View 1")
}
.isDetailLink(false)

Все остальные ссылки:

NavigationLink(destination: DetailView2().environmentObject(appStateControl)) {
    Text("Show Detail View 2")
}
.isDetailLink(false)

Тогда просто установите isDetailActive к false чтобы открыть все ваши ссылки для навигации и вернуться в главное представление:

Button(action: {
    self.appStateControl.callView = "AppMain"
    self.appStateControl.isDetailActive = false // <- add this
}) {
    Text("Go to main App")
}
Другие вопросы по тегам